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:36 UTC

[couchdb-escodegen] 05/11: script upgrade

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 590f4f89abadf2a3f091b9a783bbfcaf5f7baea7
Author: Constellation <ut...@gmail.com>
AuthorDate: Thu Jun 7 15:57:29 2012 +0900

    script upgrade
---
 assets/esprima.js | 1364 ++++++++++++++++++++++++++++++++++++-----------------
 escodegen.js      |  781 ++++++++++++++++++++++++++----
 2 files changed, 1636 insertions(+), 509 deletions(-)

diff --git a/assets/esprima.js b/assets/esprima.js
index 8a11eba..5fee256 100644
--- a/assets/esprima.js
+++ b/assets/esprima.js
@@ -28,14 +28,15 @@
   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-/*jslint bitwise:true */
+/*jslint bitwise:true plusplus:true */
 /*global esprima:true, exports:true,
 throwError: true, createLiteral: true, generateStatement: true,
 parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
 parseFunctionDeclaration: true, parseFunctionExpression: true,
 parseFunctionSourceElements: true, parseVariableIdentifier: true,
+parseImportSpecifier: true,
 parseLeftHandSideExpression: true,
-parseStatement: true, parseSourceElement: true */
+parseStatement: true, parseSourceElement: true, parseModuleBlock: true */
 
 (function (exports) {
     'use strict';
@@ -47,19 +48,14 @@ parseStatement: true, parseSourceElement: true */
         Messages,
         Regex,
         source,
-        allowIn,
-        lastParenthesized,
         strict,
         index,
         lineNumber,
         lineStart,
         length,
         buffer,
-        extra,
-        labelSet,
-        inIteration,
-        inSwitch,
-        inFunctionBody;
+        state,
+        extra;
 
     Token = {
         BooleanLiteral: 1,
@@ -85,6 +81,7 @@ parseStatement: true, parseSourceElement: true */
     Syntax = {
         AssignmentExpression: 'AssignmentExpression',
         ArrayExpression: 'ArrayExpression',
+        ArrayPattern: 'ArrayPattern',
         BlockStatement: 'BlockStatement',
         BinaryExpression: 'BinaryExpression',
         BreakStatement: 'BreakStatement',
@@ -95,21 +92,32 @@ parseStatement: true, parseSourceElement: true */
         DoWhileStatement: 'DoWhileStatement',
         DebuggerStatement: 'DebuggerStatement',
         EmptyStatement: 'EmptyStatement',
+        ExportSpecifier: 'ExportSpecifier',
+        ExportSpecifierSet: 'ExportSpecifierSet',
+        ExportDeclaration: 'ExportDeclaration',
         ExpressionStatement: 'ExpressionStatement',
         ForStatement: 'ForStatement',
         ForInStatement: 'ForInStatement',
+        ForOfStatement: 'ForOfStatement',
         FunctionDeclaration: 'FunctionDeclaration',
         FunctionExpression: 'FunctionExpression',
+        Glob: 'Glob',
         Identifier: 'Identifier',
         IfStatement: 'IfStatement',
+        ImportDeclaration: 'ImportDeclaration',
+        ImportSpecifier: 'ImportSpecifier',
         Literal: 'Literal',
         LabeledStatement: 'LabeledStatement',
         LogicalExpression: 'LogicalExpression',
         MemberExpression: 'MemberExpression',
+        ModuleDeclaration: 'ModuleDeclaration',
         NewExpression: 'NewExpression',
         ObjectExpression: 'ObjectExpression',
+        ObjectPattern: 'ObjectPattern',
+        Path:  'Path',
         Program: 'Program',
         Property: 'Property',
+        ProtoExpression: 'ProtoExpression',
         ReturnStatement: 'ReturnStatement',
         SequenceExpression: 'SequenceExpression',
         SwitchStatement: 'SwitchStatement',
@@ -140,6 +148,7 @@ parseStatement: true, parseSourceElement: true */
         UnexpectedReserved:  'Unexpected reserved word',
         UnexpectedEOS:  'Unexpected end of input',
         NewlineAfterThrow:  'Illegal newline after throw',
+        NewlineAfterModule:  'Illegal newline after module',
         InvalidRegExp: 'Invalid regular expression',
         UnterminatedRegExp:  'Invalid regular expression: missing /',
         InvalidLHSInAssignment:  'Invalid left-hand side in assignment',
@@ -164,7 +173,8 @@ parseStatement: true, parseSourceElement: true */
         StrictLHSAssignment:  'Assignment to eval or arguments is not allowed in strict mode',
         StrictLHSPostfix:  'Postfix increment/decrement may not have eval or arguments operand in strict mode',
         StrictLHSPrefix:  'Prefix increment/decrement may not have eval or arguments operand in strict mode',
-        StrictReservedWord:  'Use of future reserved word in strict mode'
+        StrictReservedWord:  'Use of future reserved word in strict mode',
+        NoFromAfterImport: 'Missing from after import'
     };
 
     // See also tools/generate-unicode-regex.py.
@@ -173,15 +183,6 @@ parseStatement: true, parseSourceElement: true */
         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 [...]
     };
 
-    if (typeof Object.freeze === 'function') {
-        Object.freeze(Token);
-        Object.freeze(TokenName);
-        Object.freeze(Syntax);
-        Object.freeze(PropertyKind);
-        Object.freeze(Messages);
-        Object.freeze(Regex);
-    }
-
     // Ensure the condition is true, otherwise throw an error.
     // This is only to have a better contract semantic, i.e. another safety net
     // to catch a logic error. The condition shall be fulfilled in normal case.
@@ -338,18 +339,18 @@ parseStatement: true, parseSourceElement: true */
             return true;
         }
 
+        // Harmony
+        if (id === 'module') {
+            return true;
+        }
+
         return isFutureReservedWord(id);
     }
 
     // Return the next character and move forward.
 
     function nextChar() {
-        var ch;
-        if (index < length) {
-            ch = source[index];
-            index += 1;
-        }
-        return ch;
+        return source[index++];
     }
 
     // 7.4 Comments
@@ -368,18 +369,18 @@ parseStatement: true, parseSourceElement: true */
                 if (isLineTerminator(ch)) {
                     lineComment = false;
                     if (ch === '\r' && source[index] === '\n') {
-                        index += 1;
+                        ++index;
                     }
-                    lineNumber += 1;
+                    ++lineNumber;
                     lineStart = index;
                 }
             } else if (blockComment) {
                 if (isLineTerminator(ch)) {
                     if (ch === '\r' && source[index + 1] === '\n') {
-                        index += 1;
+                        ++index;
                     }
-                    lineNumber += 1;
-                    index += 1;
+                    ++lineNumber;
+                    ++index;
                     lineStart = index;
                     if (index >= length) {
                         throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
@@ -392,7 +393,7 @@ parseStatement: true, parseSourceElement: true */
                     if (ch === '*') {
                         ch = source[index];
                         if (ch === '/') {
-                            index += 1;
+                            ++index;
                             blockComment = false;
                         }
                     }
@@ -412,13 +413,13 @@ parseStatement: true, parseSourceElement: true */
                     break;
                 }
             } else if (isWhiteSpace(ch)) {
-                index += 1;
+                ++index;
             } else if (isLineTerminator(ch)) {
-                index += 1;
+                ++index;
                 if (ch ===  '\r' && source[index] === '\n') {
-                    index += 1;
+                    ++index;
                 }
-                lineNumber += 1;
+                ++lineNumber;
                 lineStart = index;
             } else {
                 break;
@@ -430,7 +431,7 @@ parseStatement: true, parseSourceElement: true */
         var i, len, ch, code = 0;
 
         len = (prefix === 'u') ? 4 : 2;
-        for (i = 0; i < len; i += 1) {
+        for (i = 0; i < len; ++i) {
             if (index < length && isHexDigit(source[index])) {
                 ch = nextChar();
                 code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
@@ -451,11 +452,11 @@ parseStatement: true, parseSourceElement: true */
 
         start = index;
         if (ch === '\\') {
-            index += 1;
+            ++index;
             if (source[index] !== 'u') {
                 return;
             }
-            index += 1;
+            ++index;
             restore = index;
             ch = scanHexEscape('u');
             if (ch) {
@@ -477,11 +478,11 @@ parseStatement: true, parseSourceElement: true */
                 break;
             }
             if (ch === '\\') {
-                index += 1;
+                ++index;
                 if (source[index] !== 'u') {
                     return;
                 }
-                index += 1;
+                ++index;
                 restore = index;
                 ch = scanHexEscape('u');
                 if (ch) {
@@ -565,7 +566,7 @@ parseStatement: true, parseSourceElement: true */
         // Check for most common single-character punctuators.
 
         if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
-            index += 1;
+            ++index;
             return {
                 type: Token.Punctuator,
                 value: ch1,
@@ -576,7 +577,7 @@ parseStatement: true, parseSourceElement: true */
         }
 
         if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
-            index += 1;
+            ++index;
             return {
                 type: Token.Punctuator,
                 value: ch1,
@@ -706,9 +707,31 @@ parseStatement: true, parseSourceElement: true */
             }
         }
 
+        if (ch1 === '<' && ch2 === '|') {
+            index += 2;
+            return {
+                type: Token.Punctuator,
+                value: '<|',
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                range: [start, index]
+            };
+        }
+
+        if (ch1 === '=' && ch2 === '>') {
+            index += 2;
+            return {
+                type: Token.Punctuator,
+                value: '=>',
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                range: [start, index]
+            };
+        }
+
         // The remaining 1-character punctuators.
 
-        if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
+        if ('[]<>+-*%&|^!~?:=#/'.indexOf(ch1) >= 0) {
             return {
                 type: Token.Punctuator,
                 value: nextChar(),
@@ -722,7 +745,7 @@ parseStatement: true, parseSourceElement: true */
     // 7.8.3 Numeric Literals
 
     function scanNumericLiteral() {
-        var number, start, ch;
+        var number, start, ch, octal;
 
         ch = source[index];
         assert(isDecimalDigit(ch) || (ch === '.'),
@@ -736,6 +759,8 @@ parseStatement: true, parseSourceElement: true */
 
             // Hex number starts with '0x'.
             // Octal number starts with '0'.
+            // Octal number in ES6 starts with '0o'.
+            // Binary number in ES6 starts with '0b'.
             if (number === '0') {
                 if (ch === 'x' || ch === 'X') {
                     number += nextChar();
@@ -765,8 +790,46 @@ parseStatement: true, parseSourceElement: true */
                         lineStart: lineStart,
                         range: [start, index]
                     };
-                } else if (isOctalDigit(ch)) {
-                    number += nextChar();
+                } else if (ch === 'b' || ch === 'B') {
+                    nextChar();
+                    number = '';
+
+                    while (index < length) {
+                        ch = source[index];
+                        if (ch !== '0' && ch !== '1') {
+                            break;
+                        }
+                        number += nextChar();
+                    }
+
+                    if (number.length === 0) {
+                        // only 0b or 0B
+                        throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                    }
+
+                    if (index < length) {
+                        ch = source[index];
+                        if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
+                            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                        }
+                    }
+                    return {
+                        type: Token.NumericLiteral,
+                        value: parseInt(number, 2),
+                        lineNumber: lineNumber,
+                        lineStart: lineStart,
+                        range: [start, index]
+                    };
+                } else if (ch === 'o' || ch === 'O' || isOctalDigit(ch)) {
+                    if (isOctalDigit(ch)) {
+                        octal = true;
+                        number = nextChar();
+                    } else {
+                        octal = false;
+                        nextChar();
+                        number = '';
+                    }
+
                     while (index < length) {
                         ch = source[index];
                         if (!isOctalDigit(ch)) {
@@ -775,16 +838,22 @@ parseStatement: true, parseSourceElement: true */
                         number += nextChar();
                     }
 
+                    if (number.length === 0) {
+                        // only 0o or 0O
+                        throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                    }
+
                     if (index < length) {
                         ch = source[index];
                         if (isIdentifierStart(ch) || isDecimalDigit(ch)) {
                             throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
                         }
                     }
+
                     return {
                         type: Token.NumericLiteral,
                         value: parseInt(number, 8),
-                        octal: true,
+                        octal: octal,
                         lineNumber: lineNumber,
                         lineStart: lineStart,
                         range: [start, index]
@@ -870,7 +939,7 @@ parseStatement: true, parseSourceElement: true */
             'String literal must starts with a quote');
 
         start = index;
-        index += 1;
+        ++index;
 
         while (index < length) {
             ch = nextChar();
@@ -940,9 +1009,9 @@ parseStatement: true, parseSourceElement: true */
                         break;
                     }
                 } else {
-                    lineNumber += 1;
+                    ++lineNumber;
                     if (ch ===  '\r' && source[index] === '\n') {
-                        index += 1;
+                        ++index;
                     }
                 }
             } else if (isLineTerminator(ch)) {
@@ -986,15 +1055,17 @@ parseStatement: true, parseSourceElement: true */
                 }
             } else {
                 if (ch === '\\') {
-                    str += nextChar();
-                }
-                if (ch === '/') {
+                    ch = nextChar();
+                    // ECMA-262 7.8.5
+                    if (isLineTerminator(ch)) {
+                        throwError({}, Messages.UnterminatedRegExp);
+                    }
+                    str += ch;
+                } else if (ch === '/') {
                     break;
-                }
-                if (ch === '[') {
+                } else if (ch === '[') {
                     classMarker = true;
-                }
-                if (isLineTerminator(ch)) {
+                } else if (isLineTerminator(ch)) {
                     throwError({}, Messages.UnterminatedRegExp);
                 }
             }
@@ -1014,17 +1085,17 @@ parseStatement: true, parseSourceElement: true */
                 break;
             }
 
-            index += 1;
+            ++index;
             if (ch === '\\' && index < length) {
                 ch = source[index];
                 if (ch === 'u') {
-                    index += 1;
+                    ++index;
                     restore = index;
                     ch = scanHexEscape('u');
                     if (ch) {
                         flags += ch;
                         str += '\\u';
-                        for (; restore < index; restore += 1) {
+                        for (; restore < index; ++restore) {
                             str += source[restore];
                         }
                     } else {
@@ -1176,6 +1247,20 @@ parseStatement: true, parseSourceElement: true */
         throw error;
     }
 
+    function throwErrorTolerant() {
+        var error;
+        try {
+            throwError.apply(null, arguments);
+        } catch (e) {
+            if (extra.errors) {
+                extra.errors.push(e);
+            } else {
+                throw e;
+            }
+        }
+    }
+
+
     // Throw an exception because of the token.
 
     function throwUnexpected(token) {
@@ -1244,6 +1329,14 @@ parseStatement: true, parseSourceElement: true */
         return token.type === Token.Keyword && token.value === keyword;
     }
 
+
+    // Return true if the next token matches the specified contextual keyword
+
+    function matchContextualKeyword(keyword) {
+        var token = lookahead();
+        return token.type === Token.Identifier && token.value === keyword;
+    }
+
     // Return true if the next token is an assignment operator
 
     function matchAssign() {
@@ -1339,6 +1432,12 @@ parseStatement: true, parseSourceElement: true */
         };
     }
 
+    function parseSealedArrayInitialiser() {
+        var result = parseArrayInitialiser();
+        result.sealed = true;
+        return result;
+    }
+
     // 11.1.5 Object Initialiser
 
     function parsePropertyFunction(param, first) {
@@ -1359,61 +1458,85 @@ parseStatement: true, parseSourceElement: true */
         };
     }
 
+    function parsePropertyMethodFunction() {
+        var token, previousStrict, param, params, paramSet, method;
+
+        previousStrict = strict;
+        strict = true;
+        params = [];
+
+        expect('(');
+
+        if (!match(')')) {
+            paramSet = {};
+            while (index < length) {
+                token = lookahead();
+                param = parseVariableIdentifier();
+                if (isRestrictedWord(token.value)) {
+                    throwError(token, Messages.StrictParamName);
+                }
+                if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) {
+                    throwError(token, Messages.StrictParamDupe);
+                }
+                params.push(param);
+                paramSet[param.name] = true;
+                if (match(')')) {
+                    break;
+                }
+                expect(',');
+            }
+        }
+
+        expect(')');
+
+        method = parsePropertyFunction(params);
+
+        strict = previousStrict;
+
+        return method;
+    }
+
     function parseObjectPropertyKey() {
-        var token = lex(),
-            key;
+        var token = lex();
 
-        switch (token.type) {
+        // Note: This function is called only from parseObjectProperty(), where
+        // EOF and Punctuator tokens are already filtered out.
 
-        case Token.StringLiteral:
-        case Token.NumericLiteral:
+        if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
             if (strict && token.octal) {
                 throwError(token, Messages.StrictOctalLiteral);
             }
-            key = createLiteral(token);
-            break;
-
-        case Token.Identifier:
-        case Token.Keyword:
-        case Token.BooleanLiteral:
-        case Token.NullLiteral:
-            key = {
-                type: Syntax.Identifier,
-                name: token.value
-            };
-            break;
-
-        default:
-            // Unreachable, since parseObjectProperty() will not call this
-            // function with any other type of token.
+            return createLiteral(token);
         }
 
-        return key;
+        return {
+            type: Syntax.Identifier,
+            name: token.value
+        };
     }
 
     function parseObjectProperty() {
-        var token, property, key, id, param;
+        var token, key, id, param;
 
         token = lookahead();
 
-        switch (token.type) {
+        if (token.type === Token.Identifier) {
 
-        case Token.Identifier:
             id = parseObjectPropertyKey();
 
             // Property Assignment: Getter and Setter.
 
-            if (token.value === 'get' && !match(':')) {
+            if (token.value === 'get' && !(match(':') || match('('))) {
                 key = parseObjectPropertyKey();
                 expect('(');
                 expect(')');
-                property = {
+                return {
                     type: Syntax.Property,
                     key: key,
                     value: parsePropertyFunction([]),
                     kind: 'get'
                 };
-            } else if (token.value === 'set' && !match(':')) {
+            } else if (token.value === 'set' && !(match(':') || match('('))) {
                 key = parseObjectPropertyKey();
                 expect('(');
                 token = lookahead();
@@ -1422,43 +1545,69 @@ parseStatement: true, parseSourceElement: true */
                 }
                 param = [ parseVariableIdentifier() ];
                 expect(')');
-                property = {
+                return {
                     type: Syntax.Property,
                     key: key,
                     value: parsePropertyFunction(param, token),
                     kind: 'set'
                 };
             } else {
-                expect(':');
-                property = {
+                if (match(':')) {
+                    lex();
+                    return {
+                        type: Syntax.Property,
+                        key: id,
+                        value: parseAssignmentExpression(),
+                        kind: 'init'
+                    };
+                } else if (match('(')) {
+                    return {
+                        type: Syntax.Property,
+                        key: id,
+                        value: parsePropertyMethodFunction(),
+                        kind: 'init',
+                        method: true
+                    };
+                } else {
+                    return {
+                        type: Syntax.Property,
+                        key: id,
+                        value: id,
+                        kind: 'init',
+                        shorthand: true
+                    };
+                }
+            }
+        } else if (token.type === Token.EOF || token.type === Token.Punctuator) {
+            throwUnexpected(token);
+        } else {
+            key = parseObjectPropertyKey();
+            if (match(':')) {
+                lex();
+                return {
                     type: Syntax.Property,
-                    key: id,
+                    key: key,
                     value: parseAssignmentExpression(),
                     kind: 'init'
                 };
+            } else if (match('(')) {
+                return {
+                    type: Syntax.Property,
+                    key: key,
+                    value: parsePropertyMethodFunction(),
+                    kind: 'init',
+                    method: true
+                };
+            } else {
+                return {
+                    type: Syntax.Property,
+                    key: key,
+                    value: key,
+                    kind: 'init',
+                    shorthand: true
+                };
             }
-            break;
-
-        case Token.Keyword:
-        case Token.BooleanLiteral:
-        case Token.NullLiteral:
-        case Token.StringLiteral:
-        case Token.NumericLiteral:
-            key = parseObjectPropertyKey();
-            expect(':');
-            property = {
-                type: Syntax.Property,
-                key: key,
-                value: parseAssignmentExpression(),
-                kind: 'init'
-            };
-            break;
-
-        default:
-            throwUnexpected(token);
         }
-
-        return property;
     }
 
     function parseObjectInitialiser() {
@@ -1478,7 +1627,7 @@ parseStatement: true, parseSourceElement: true */
             if (Object.prototype.hasOwnProperty.call(map, name)) {
                 if (map[name] === PropertyKind.Data) {
                     if (strict && kind === PropertyKind.Data) {
-                        throwError({}, Messages.StrictDuplicateProperty);
+                        throwErrorTolerant({}, Messages.StrictDuplicateProperty);
                     } else if (kind !== PropertyKind.Data) {
                         throwError({}, Messages.AccessorDataProperty);
                     }
@@ -1509,6 +1658,12 @@ parseStatement: true, parseSourceElement: true */
         };
     }
 
+    function parseSealedObjectInitialiser() {
+        var result = parseObjectInitialiser();
+        result.sealed = true;
+        return result;
+    }
+
     // 11.1 Primary Expressions
 
     function parsePrimaryExpression() {
@@ -1525,7 +1680,7 @@ parseStatement: true, parseSourceElement: true */
 
         if (type === Token.StringLiteral || type === Token.NumericLiteral) {
             if (strict && token.octal) {
-                throwError(token, Messages.StrictOctalLiteral);
+                throwErrorTolerant(token, Messages.StrictOctalLiteral);
             }
             return createLiteral(lex());
         }
@@ -1565,7 +1720,7 @@ parseStatement: true, parseSourceElement: true */
 
         if (match('(')) {
             lex();
-            lastParenthesized = expr = parseExpression();
+            state.lastParenthesized = expr = parseExpression();
             expect(')');
             return expr;
         }
@@ -1574,6 +1729,17 @@ parseStatement: true, parseSourceElement: true */
             return createLiteral(scanRegExp());
         }
 
+        if (match('#')) {
+            lex();
+            if (match('[')) {
+                return parseSealedArrayInitialiser();
+            }
+
+            if (match('{')) {
+                return parseSealedObjectInitialiser();
+            }
+        }
+
         return throwUnexpected(lex());
     }
 
@@ -1662,6 +1828,57 @@ parseStatement: true, parseSourceElement: true */
         return expr;
     }
 
+    function parseTriangleLiteral() {
+        var expr,
+            token = lookahead(),
+            type = token.type;
+
+        if (type === Token.StringLiteral || type === Token.NumericLiteral) {
+            if (strict && token.octal) {
+                throwErrorTolerant(token, Messages.StrictOctalLiteral);
+            }
+            return createLiteral(lex());
+        }
+
+        if (type === Token.Keyword) {
+            if (matchKeyword('function')) {
+                return parseFunctionExpression();
+            }
+        }
+
+        if (type === Token.BooleanLiteral) {
+            lex();
+            token.value = (token.value === 'true');
+            return createLiteral(token);
+        }
+
+        if (match('[')) {
+            return parseArrayInitialiser();
+        }
+
+        if (match('{')) {
+            return parseObjectInitialiser();
+        }
+
+        if (match('/') || match('/=')) {
+            return createLiteral(scanRegExp());
+        }
+
+        // ArrowFunctionExpression
+        expr = parseExpression();
+
+        return parseArrowFunctionExpression(expr);
+    }
+
+    function parseProtoExpression(proto) {
+        expect('<|');
+        return {
+            type: Syntax.ProtoExpression,
+            proto: proto,
+            literal: parseTriangleLiteral()
+        };
+    }
+
     function parseLeftHandSideExpressionAllowCall() {
         var useNew, expr;
 
@@ -1674,6 +1891,8 @@ parseStatement: true, parseSourceElement: true */
                 expr = parseNonComputedMember(expr);
             } else if (match('[')) {
                 expr = parseComputedMember(expr);
+            } else if (match('<|')) {
+                expr = parseProtoExpression(expr);
             } else if (match('(')) {
                 expr = parseCallMember(expr);
             } else {
@@ -1696,6 +1915,8 @@ parseStatement: true, parseSourceElement: true */
                 expr = parseNonComputedMember(expr);
             } else if (match('[')) {
                 expr = parseComputedMember(expr);
+            } else if (match('<|')) {
+                expr = parseProtoExpression(expr);
             } else {
                 break;
             }
@@ -1762,7 +1983,7 @@ parseStatement: true, parseSourceElement: true */
                 argument: parseUnaryExpression()
             };
             if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
-                throwError({}, Messages.StrictDelete);
+                throwErrorTolerant({}, Messages.StrictDelete);
             }
             return expr;
         }
@@ -1825,10 +2046,10 @@ parseStatement: true, parseSourceElement: true */
     function parseRelationalExpression() {
         var expr, previousAllowIn;
 
-        previousAllowIn = allowIn;
-        allowIn = true;
+        previousAllowIn = state.allowIn;
+        state.allowIn = true;
         expr = parseShiftExpression();
-        allowIn = previousAllowIn;
+        state.allowIn = previousAllowIn;
 
         if (match('<') || match('>') || match('<=') || match('>=')) {
             expr = {
@@ -1837,7 +2058,7 @@ parseStatement: true, parseSourceElement: true */
                 left: expr,
                 right: parseRelationalExpression()
             };
-        } else if (allowIn && matchKeyword('in')) {
+        } else if (state.allowIn && matchKeyword('in')) {
             lex();
             expr = {
                 type: Syntax.BinaryExpression,
@@ -1863,7 +2084,7 @@ parseStatement: true, parseSourceElement: true */
     function parseEqualityExpression() {
         var expr = parseRelationalExpression();
 
-        while (match('==') || match('!=') || match('===') || match('!==')) {
+        while ((!peekLineTerminator() && (matchContextualKeyword('is') || matchContextualKeyword('isnt'))) || match('==') || match('!=') || match('===') || match('!==')) {
             expr = {
                 type: Syntax.BinaryExpression,
                 operator: lex().value,
@@ -1968,10 +2189,10 @@ parseStatement: true, parseSourceElement: true */
 
         if (match('?')) {
             lex();
-            previousAllowIn = allowIn;
-            allowIn = true;
+            previousAllowIn = state.allowIn;
+            state.allowIn = true;
             consequent = parseAssignmentExpression();
-            allowIn = previousAllowIn;
+            state.allowIn = previousAllowIn;
             expect(':');
 
             expr = {
@@ -1987,6 +2208,81 @@ parseStatement: true, parseSourceElement: true */
 
     // 11.13 Assignment Operators
 
+    function reinterpretAsAssignmentBindingPattern(expr) {
+        var i, len, property, element;
+
+        if (expr.sealed) {
+            throwError({}, Messages.InvalidLHSInAssignment);
+        }
+
+        if (expr.type === Syntax.ObjectExpression) {
+            expr.type = Syntax.ObjectPattern;
+            for (i = 0, len = expr.properties.length; i < len; i += 1) {
+                property = expr.properties[i];
+                if (property.kind !== 'init') {
+                    throwError({}, Messages.InvalidLHSInAssignment);
+                }
+                reinterpretAsAssignmentBindingPattern(property.value);
+            }
+        } else if (expr.type === Syntax.ArrayExpression) {
+            expr.type = Syntax.ArrayPattern;
+            for (i = 0, len = expr.elements.length; i < len; i += 1) {
+                element = expr.elements[i];
+                if (element) {
+                    reinterpretAsAssignmentBindingPattern(element);
+                }
+            }
+        } else if (expr.type === Syntax.Identifier) {
+            if (isRestrictedWord(expr.name)) {
+                throwError({}, Messages.InvalidLHSInAssignment);
+            }
+        } else {
+            if (expr.type !== Syntax.MemberExpression && expr.type !== Syntax.CallExpression && expr.type !== Syntax.NewExpression) {
+                throwError({}, Messages.InvalidLHSInAssignment);
+            }
+        }
+    }
+
+    function reinterpretAsCoverFormalsList(expr) {
+        if (state.lastParenthesized !== expr) {
+            if (expr.type === Syntax.Identifier) {
+                // e => ConciseBody style
+                return [ expr ];
+            }
+            throwError({}, Messages.InvalidCoverFormalsList);
+        }
+
+        if (expr.type === Syntax.Identifier) {
+            // (e) => ConciseBody style
+            return [ expr ];
+        }
+
+        if (expr.type !== Syntax.SequenceExpression) {
+            throwError({}, Messages.InvalidCoverFormalsList);
+        }
+
+        return expr;
+    }
+
+    function parseArrowFunctionExpression(expr) {
+        var params, previousStrict;
+
+        params = reinterpretAsCoverFormalsList(expr);
+
+        expect('=>');
+
+        previousStrict = strict;
+        strict = true;
+        expr = {
+            type: Syntax.ArrowFunctionExpression,
+            params: params,
+            body: parseConciseBody()
+        };
+        strict = previousStrict;
+
+        return expr;
+    }
+
     function parseAssignmentExpression() {
         var expr;
 
@@ -1994,7 +2290,7 @@ parseStatement: true, parseSourceElement: true */
 
         if (matchAssign()) {
             // LeftHandSideExpression
-            if (lastParenthesized !== expr && !isLeftHandSide(expr)) {
+            if (state.lastParenthesized !== expr && !isLeftHandSide(expr)) {
                 throwError({}, Messages.InvalidLHSInAssignment);
             }
 
@@ -2003,12 +2299,20 @@ parseStatement: true, parseSourceElement: true */
                 throwError({}, Messages.StrictLHSAssignment);
             }
 
+            // ES.next draf 11.13 Runtime Semantics step 1
+            if (expr.type === Syntax.ObjectExpression || expr.type === Syntax.ArrayExpression) {
+                reinterpretAsAssignmentBindingPattern(expr);
+            }
+
             expr = {
                 type: Syntax.AssignmentExpression,
                 operator: lex().value,
                 left: expr,
                 right: parseAssignmentExpression()
             };
+        } else if (match('=>')) {
+            lex();
+            return parseArrowFunctionExpression(expr);
         }
 
         return expr;
@@ -2093,7 +2397,7 @@ parseStatement: true, parseSourceElement: true */
 
         // 12.2.1
         if (strict && isRestrictedWord(id.name)) {
-            throwError({}, Messages.StrictVarName);
+            throwErrorTolerant({}, Messages.StrictVarName);
         }
 
         if (kind === 'const') {
@@ -2161,17 +2465,255 @@ parseStatement: true, parseSourceElement: true */
         };
     }
 
-    // 12.3 Empty Statement
+    // http://wiki.ecmascript.org/doku.php?id=harmony:modules
 
-    function parseEmptyStatement() {
-        expect(';');
+    function parsePath() {
+        var result, id;
 
-        return {
-            type: Syntax.EmptyStatement
+        result = {
+            type: Syntax.Path,
+            body: []
         };
-    }
 
-    // 12.4 Expression Statement
+        while (true) {
+            id = parseVariableIdentifier();
+            result.body.push(id);
+            if (!match('.')) {
+                break;
+            }
+            lex();
+        }
+
+        return result;
+    }
+
+    function parseGlob() {
+        expect('*');
+        return {
+            type: Syntax.Glob
+        };
+    }
+
+    function parseModuleDeclaration() {
+        var id, token, declaration;
+
+        expectKeyword('module');
+
+        if (peekLineTerminator()) {
+            throwError({}, Messages.NewlineAfterModule);
+        }
+
+        id = parseVariableIdentifier();
+
+        if (match('{')) {
+            return {
+                type: Syntax.ModuleDeclaration,
+                id: id,
+                body: parseModuleBlock()
+            };
+        }
+
+        expect('=');
+
+        token = lookahead();
+        if (token.type === Token.StringLiteral) {
+            declaration = {
+                type: Syntax.ModuleDeclaration,
+                id: id,
+                from: parsePrimaryExpression()
+            };
+        } else {
+            declaration = {
+                type: Syntax.ModuleDeclaration,
+                id: id,
+                from: parsePath()
+            };
+        }
+
+        consumeSemicolon();
+
+        return declaration;
+    }
+
+    function parseExportSpecifierSetProperty() {
+        var specifier;
+
+        specifier = {
+            type: Syntax.ExportSpecifier,
+            id: parseVariableIdentifier(),
+            from: null
+        };
+
+        if (match(':')) {
+            lex();
+            specifier.from = parsePath();
+        }
+
+        return specifier;
+    }
+
+    function parseExportSpecifier() {
+        var specifier, specifiers;
+
+        if (match('{')) {
+            lex();
+            specifiers = [];
+
+            do {
+                specifiers.push(parseExportSpecifierSetProperty());
+            } while (match(',') && lex());
+
+            expect('}');
+
+            return {
+                type: Syntax.ExportSpecifierSet,
+                specifiers: specifiers
+            };
+        }
+
+        if (match('*')) {
+            specifier = {
+                type: Syntax.ExportSpecifier,
+                id: parseGlob(),
+                from: null
+            };
+
+            if (matchContextualKeyword('from')) {
+                lex();
+                specifier.from = parsePath();
+            }
+        } else {
+            specifier = {
+                type: Syntax.ExportSpecifier,
+                id: parseVariableIdentifier(),
+                from: null
+            };
+        }
+        return specifier;
+    }
+
+    function parseExportDeclaration() {
+        var id, token, declaration, specifiers;
+
+        expectKeyword('export');
+
+        token = lookahead();
+
+        if (token.type === Token.Keyword) {
+            switch (token.value) {
+            case 'function':
+                return {
+                    type: Syntax.ExportDeclaration,
+                    declaration: parseFunctionDeclaration()
+                };
+            case 'module':
+                return {
+                    type: Syntax.ExportDeclaration,
+                    declaration: parseModuleDeclaration()
+                };
+            case 'let':
+            case 'const':
+                return {
+                    type: Syntax.ExportDeclaration,
+                    declaration: parseConstLetDeclaration(token.value)
+                };
+            case 'var':
+                return {
+                    type: Syntax.ExportDeclaration,
+                    declaration: parseStatement()
+                };
+            }
+            throwUnexpected(lex());
+        }
+
+        specifiers = [ parseExportSpecifier() ];
+        if (match(',')) {
+            while (index < length) {
+                if (!match(',')) {
+                    break;
+                }
+                lex();
+                specifiers.push(parseExportSpecifier());
+            }
+        }
+
+        consumeSemicolon();
+
+        return {
+            type: Syntax.ExportDeclaration,
+            specifiers: specifiers
+        };
+    }
+
+    function parseImportDeclaration() {
+        var specifiers, from;
+
+        expectKeyword('import');
+
+        if (match('*')) {
+            specifiers = [parseGlob()];
+        } else if (match('{')) {
+            lex();
+            specifiers = [];
+
+            do {
+                specifiers.push(parseImportSpecifier());
+            } while (match(',') && lex());
+
+            expect('}');
+        } else {
+            specifiers = [parseVariableIdentifier()];
+        }
+
+        if (!matchContextualKeyword('from')) {
+            throwError({}, Messages.NoFromAfterImport);
+        }
+
+        lex();
+
+        if (lookahead().type === Token.StringLiteral) {
+            from = parsePrimaryExpression();
+        } else {
+            from = parsePath();
+        }
+
+        consumeSemicolon();
+
+        return {
+            type: Syntax.ImportDeclaration,
+            specifiers: specifiers,
+            from: from
+        };
+    }
+
+    function parseImportSpecifier() {
+        var specifier;
+
+        specifier = {
+            type: Syntax.ImportSpecifier,
+            id: parseVariableIdentifier(),
+            from: null
+        };
+
+        if (match(':')) {
+            lex();
+            specifier.from = parsePath();
+        }
+
+        return specifier;
+    }
+
+    // 12.3 Empty Statement
+
+    function parseEmptyStatement() {
+        expect(';');
+
+        return {
+            type: Syntax.EmptyStatement
+        };
+    }
+
+    // 12.4 Expression Statement
 
     function parseExpressionStatement() {
         var expr = parseExpression();
@@ -2221,12 +2763,12 @@ parseStatement: true, parseSourceElement: true */
 
         expectKeyword('do');
 
-        oldInIteration = inIteration;
-        inIteration = true;
+        oldInIteration = state.inIteration;
+        state.inIteration = true;
 
         body = parseStatement();
 
-        inIteration = oldInIteration;
+        state.inIteration = oldInIteration;
 
         expectKeyword('while');
 
@@ -2258,12 +2800,12 @@ parseStatement: true, parseSourceElement: true */
 
         expect(')');
 
-        oldInIteration = inIteration;
-        inIteration = true;
+        oldInIteration = state.inIteration;
+        state.inIteration = true;
 
         body = parseStatement();
 
-        inIteration = oldInIteration;
+        state.inIteration = oldInIteration;
 
         return {
             type: Syntax.WhileStatement,
@@ -2283,7 +2825,7 @@ parseStatement: true, parseSourceElement: true */
     }
 
     function parseForStatement() {
-        var init, test, update, left, right, body, oldInIteration;
+        var init, test, update, left, right, body, operator, oldInIteration, i, len;
 
         init = test = update = null;
 
@@ -2294,28 +2836,38 @@ parseStatement: true, parseSourceElement: true */
         if (match(';')) {
             lex();
         } else {
-            if (matchKeyword('var') || matchKeyword('let')) {
-                allowIn = false;
+            if (matchKeyword('var') || matchKeyword('let') || matchKeyword('const')) {
+                state.allowIn = false;
                 init = parseForVariableDeclaration();
-                allowIn = true;
-
-                if (init.declarations.length === 1 && matchKeyword('in')) {
-                    lex();
-                    left = init;
-                    right = parseExpression();
-                    init = null;
+                state.allowIn = true;
+
+                if (init.declarations.length === 1) {
+                    if (matchKeyword('in') || matchContextualKeyword('of')) {
+                        operator = lookahead();
+                        if (!((operator.value === 'in' || init.kind !== 'var') && init.declarations[0].init)) {
+                            lex();
+                            left = init;
+                            right = parseExpression();
+                            init = null;
+                        }
+                    }
                 }
             } else {
-                allowIn = false;
+                state.allowIn = false;
                 init = parseExpression();
-                allowIn = true;
+                state.allowIn = true;
 
-                if (matchKeyword('in')) {
+                if (matchContextualKeyword('of')) {
+                    operator = lex();
+                    left = init;
+                    right = parseExpression();
+                    init = null;
+                } else if (matchKeyword('in')) {
                     // LeftHandSideExpression
-                    if (matchKeyword('in') && (lastParenthesized !== init && !isLeftHandSide(init))) {
+                    if (matchKeyword('in') && (state.lastParenthesized !== init && !isLeftHandSide(init))) {
                         throwError({}, Messages.InvalidLHSInForIn);
                     }
-                    lex();
+                    operator = lex();
                     left = init;
                     right = parseExpression();
                     init = null;
@@ -2341,12 +2893,12 @@ parseStatement: true, parseSourceElement: true */
 
         expect(')');
 
-        oldInIteration = inIteration;
-        inIteration = true;
+        oldInIteration = state.inIteration;
+        state.inIteration = true;
 
         body = parseStatement();
 
-        inIteration = oldInIteration;
+        state.inIteration = oldInIteration;
 
         if (typeof left === 'undefined') {
             return {
@@ -2358,13 +2910,23 @@ parseStatement: true, parseSourceElement: true */
             };
         }
 
-        return {
-            type: Syntax.ForInStatement,
-            left: left,
-            right: right,
-            body: body,
-            each: false
-        };
+        if (operator.value === 'in') {
+            return {
+                type: Syntax.ForInStatement,
+                left: left,
+                right: right,
+                body: body,
+                each: false
+            };
+        } else {
+            return {
+                type: Syntax.ForOfStatement,
+                left: left,
+                right: right,
+                body: body,
+                each: false
+            };
+        }
     }
 
     // 12.7 The continue statement
@@ -2377,8 +2939,8 @@ parseStatement: true, parseSourceElement: true */
         // Optimize the most common form: 'continue;'.
         if (source[index] === ';') {
             lex();
-            
-            if (!inIteration) {
+
+            if (!state.inIteration) {
                 throwError({}, Messages.IllegalContinue);
             }
 
@@ -2389,7 +2951,7 @@ parseStatement: true, parseSourceElement: true */
         }
 
         if (peekLineTerminator()) {
-            if (!inIteration) {
+            if (!state.inIteration) {
                 throwError({}, Messages.IllegalContinue);
             }
 
@@ -2403,14 +2965,14 @@ parseStatement: true, parseSourceElement: true */
         if (token.type === Token.Identifier) {
             label = parseVariableIdentifier();
 
-            if (!Object.prototype.hasOwnProperty.call(labelSet, label.name)) {
+            if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
                 throwError({}, Messages.UnknownLabel, label.name);
             }
         }
 
         consumeSemicolon();
 
-        if (label === null && !inIteration) {
+        if (label === null && !state.inIteration) {
             throwError({}, Messages.IllegalContinue);
         }
 
@@ -2430,8 +2992,8 @@ parseStatement: true, parseSourceElement: true */
         // Optimize the most common form: 'break;'.
         if (source[index] === ';') {
             lex();
-            
-            if (!(inIteration || inSwitch)) {
+
+            if (!(state.inIteration || state.inSwitch)) {
                 throwError({}, Messages.IllegalBreak);
             }
 
@@ -2442,7 +3004,7 @@ parseStatement: true, parseSourceElement: true */
         }
 
         if (peekLineTerminator()) {
-            if (!(inIteration || inSwitch)) {
+            if (!(state.inIteration || state.inSwitch)) {
                 throwError({}, Messages.IllegalBreak);
             }
 
@@ -2456,14 +3018,14 @@ parseStatement: true, parseSourceElement: true */
         if (token.type === Token.Identifier) {
             label = parseVariableIdentifier();
 
-            if (!Object.prototype.hasOwnProperty.call(labelSet, label.name)) {
+            if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) {
                 throwError({}, Messages.UnknownLabel, label.name);
             }
         }
 
         consumeSemicolon();
 
-        if (label === null && !(inIteration || inSwitch)) {
+        if (label === null && !(state.inIteration || state.inSwitch)) {
             throwError({}, Messages.IllegalBreak);
         }
 
@@ -2480,8 +3042,8 @@ parseStatement: true, parseSourceElement: true */
 
         expectKeyword('return');
 
-        if (!inFunctionBody) {
-            throwError({}, Messages.IllegalReturn);
+        if (!state.inFunctionBody) {
+            throwErrorTolerant({}, Messages.IllegalReturn);
         }
 
         // 'return' followed by a space and an identifier is very common.
@@ -2524,7 +3086,7 @@ parseStatement: true, parseSourceElement: true */
         var object, body;
 
         if (strict) {
-            throwError({}, Messages.StrictModeWith);
+            throwErrorTolerant({}, Messages.StrictModeWith);
         }
 
         expectKeyword('with');
@@ -2546,10 +3108,20 @@ parseStatement: true, parseSourceElement: true */
 
     // 12.10 The swith statement
 
-    function parseSwitchCase(test) {
-        var consequent = [],
+    function parseSwitchCase() {
+        var test,
+            consequent = [],
             statement;
 
+        if (matchKeyword('default')) {
+            lex();
+            test = null;
+        } else {
+            expectKeyword('case');
+            test = parseExpression();
+        }
+        expect(':');
+
         while (index < length) {
             if (match('}') || matchKeyword('default') || matchKeyword('case')) {
                 break;
@@ -2569,7 +3141,7 @@ parseStatement: true, parseSourceElement: true */
     }
 
     function parseSwitchStatement() {
-        var discriminant, cases, test, oldInSwitch;
+        var discriminant, cases, oldInSwitch;
 
         expectKeyword('switch');
 
@@ -2591,27 +3163,17 @@ parseStatement: true, parseSourceElement: true */
 
         cases = [];
 
-        oldInSwitch = inSwitch;
-        inSwitch = true;
+        oldInSwitch = state.inSwitch;
+        state.inSwitch = true;
 
         while (index < length) {
             if (match('}')) {
                 break;
             }
-
-            if (matchKeyword('default')) {
-                lex();
-                test = null;
-            } else {
-                expectKeyword('case');
-                test = parseExpression();
-            }
-            expect(':');
-
-            cases.push(parseSwitchCase(test));
+            cases.push(parseSwitchCase());
         }
 
-        inSwitch = oldInSwitch;
+        state.inSwitch = oldInSwitch;
 
         expect('}');
 
@@ -2655,7 +3217,7 @@ parseStatement: true, parseSourceElement: true */
             param = parseExpression();
             // 12.14.1
             if (strict && param.type === Syntax.Identifier && isRestrictedWord(param.name)) {
-                throwError({}, Messages.StrictCatchVariable);
+                throwErrorTolerant({}, Messages.StrictCatchVariable);
             }
         }
         expect(')');
@@ -2773,13 +3335,13 @@ parseStatement: true, parseSourceElement: true */
         if ((expr.type === Syntax.Identifier) && match(':')) {
             lex();
 
-            if (Object.prototype.hasOwnProperty.call(labelSet, expr.name)) {
+            if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) {
                 throwError({}, Messages.Redeclaration, 'Label', expr.name);
             }
 
-            labelSet[expr.name] = true;
+            state.labelSet[expr.name] = true;
             labeledBody = parseStatement();
-            delete labelSet[expr.name];
+            delete state.labelSet[expr.name];
 
             return {
                 type: Syntax.LabeledStatement,
@@ -2799,7 +3361,8 @@ parseStatement: true, parseSourceElement: true */
     // 13 Function Definition
 
     function parseFunctionSourceElements() {
-        var sourceElement, sourceElements = [], token, directive, firstRestricted, oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
+        var sourceElement, sourceElements = [], token, directive, firstRestricted,
+            oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody;
 
         expect('{');
 
@@ -2828,15 +3391,15 @@ parseStatement: true, parseSourceElement: true */
             }
         }
 
-        oldLabelSet = labelSet;
-        oldInIteration = inIteration;
-        oldInSwitch = inSwitch;
-        oldInFunctionBody = inFunctionBody;
+        oldLabelSet = state.labelSet;
+        oldInIteration = state.inIteration;
+        oldInSwitch = state.inSwitch;
+        oldInFunctionBody = state.inFunctionBody;
 
-        labelSet = {};
-        inIteration = false;
-        inSwitch = false;
-        inFunctionBody = true;
+        state.labelSet = {};
+        state.inIteration = false;
+        state.inSwitch = false;
+        state.inFunctionBody = true;
 
         while (index < length) {
             if (match('}')) {
@@ -2851,10 +3414,10 @@ parseStatement: true, parseSourceElement: true */
 
         expect('}');
 
-        labelSet = oldLabelSet;
-        inIteration = oldInIteration;
-        inSwitch = oldInSwitch;
-        inFunctionBody = oldInFunctionBody;
+        state.labelSet = oldLabelSet;
+        state.inIteration = oldInIteration;
+        state.inSwitch = oldInSwitch;
+        state.inFunctionBody = oldInFunctionBody;
 
         return {
             type: Syntax.BlockStatement,
@@ -3031,7 +3594,24 @@ parseStatement: true, parseSourceElement: true */
         }
     }
 
-    function parseSourceElements() {
+    function parseProgramElement() {
+        var token = lookahead();
+
+        if (token.type === Token.Keyword) {
+            switch (token.value) {
+            case 'module':
+                return parseModuleDeclaration(token.value);
+            case 'export':
+                return parseExportDeclaration();
+            case 'import':
+                return parseImportDeclaration();
+            }
+        }
+
+        return parseSourceElement();
+    }
+
+    function parseProgramElements() {
         var sourceElement, sourceElements = [], token, directive, firstRestricted;
 
         while (index < length) {
@@ -3040,7 +3620,7 @@ parseStatement: true, parseSourceElement: true */
                 break;
             }
 
-            sourceElement = parseSourceElement();
+            sourceElement = parseProgramElement();
             sourceElements.push(sourceElement);
             if (sourceElement.expression.type !== Syntax.Literal) {
                 // this is not directive
@@ -3060,7 +3640,7 @@ parseStatement: true, parseSourceElement: true */
         }
 
         while (index < length) {
-            sourceElement = parseSourceElement();
+            sourceElement = parseProgramElement();
             if (typeof sourceElement === 'undefined') {
                 break;
             }
@@ -3069,12 +3649,49 @@ parseStatement: true, parseSourceElement: true */
         return sourceElements;
     }
 
+    function parseModuleElement() {
+        return parseProgramElement();
+    }
+
+    function parseModuleElements() {
+        var list = [],
+            statement;
+
+        while (index < length) {
+            if (match('}')) {
+                break;
+            }
+            statement = parseModuleElement();
+            if (typeof statement === 'undefined') {
+                break;
+            }
+            list.push(statement);
+        }
+
+        return list;
+    }
+
+    function parseModuleBlock() {
+        var block;
+
+        expect('{');
+
+        block = parseModuleElements();
+
+        expect('}');
+
+        return {
+            type: Syntax.BlockStatement,
+            body: block
+        };
+    }
+
     function parseProgram() {
         var program;
         strict = false;
         program = {
             type: Syntax.Program,
-            body: parseSourceElements()
+            body: parseProgramElements()
         };
         return program;
     }
@@ -3083,9 +3700,7 @@ parseStatement: true, parseSourceElement: true */
     // the comments is active.
 
     function addComment(start, end, type, value) {
-        if (typeof start !== 'number') {
-            return;
-        }
+        assert(typeof start === 'number', 'Comment must have valid position');
 
         // Because the way the actual token is scanned, often the comments
         // (if any) are skipped twice during the lexical analysis.
@@ -3116,13 +3731,17 @@ parseStatement: true, parseSourceElement: true */
 
             if (lineComment) {
                 ch = nextChar();
-                if (isLineTerminator(ch)) {
+                if (index >= length) {
+                    lineComment = false;
+                    comment += ch;
+                    addComment(start, index, 'Line', comment);
+                } else if (isLineTerminator(ch)) {
                     lineComment = false;
-                    addComment(start, index - 1, 'Line', comment);
+                    addComment(start, index, 'Line', comment);
                     if (ch === '\r' && source[index] === '\n') {
-                        index += 1;
+                        ++index;
                     }
-                    lineNumber += 1;
+                    ++lineNumber;
                     lineStart = index;
                     comment = '';
                 } else {
@@ -3131,10 +3750,13 @@ parseStatement: true, parseSourceElement: true */
             } else if (blockComment) {
                 if (isLineTerminator(ch)) {
                     if (ch === '\r' && source[index + 1] === '\n') {
-                        index += 1;
+                        ++index;
+                        comment += '\r\n';
+                    } else {
+                        comment += ch;
                     }
-                    lineNumber += 1;
-                    index += 1;
+                    ++lineNumber;
+                    ++index;
                     lineStart = index;
                     if (index >= length) {
                         throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
@@ -3150,8 +3772,8 @@ parseStatement: true, parseSourceElement: true */
                         if (ch === '/') {
                             comment = comment.substr(0, comment.length - 1);
                             blockComment = false;
-                            index += 1;
-                            addComment(start, index - 1, 'Block', comment);
+                            ++index;
+                            addComment(start, index, 'Block', comment);
                             comment = '';
                         }
                     }
@@ -3173,20 +3795,18 @@ parseStatement: true, parseSourceElement: true */
                     break;
                 }
             } else if (isWhiteSpace(ch)) {
-                index += 1;
+                ++index;
             } else if (isLineTerminator(ch)) {
-                index += 1;
+                ++index;
                 if (ch ===  '\r' && source[index] === '\n') {
-                    index += 1;
+                    ++index;
                 }
-                lineNumber += 1;
+                ++lineNumber;
                 lineStart = index;
             } else {
                 break;
             }
         }
-
-        addComment(start, index, (blockComment) ? 'Block' : 'Line', comment);
     }
 
     function collectToken() {
@@ -3195,7 +3815,7 @@ parseStatement: true, parseSourceElement: true */
             value;
 
         if (token.type !== Token.EOF) {
-            range = [token.range[0], token.range[1] - 1];
+            range = [token.range[0], token.range[1]];
             value = sliceSource(token.range[0], token.range[1]);
             extra.tokens.push({
                 type: TokenName[token.type],
@@ -3228,7 +3848,7 @@ parseStatement: true, parseSourceElement: true */
         extra.tokens.push({
             type: 'RegularExpression',
             value: regex.literal,
-            range: [pos, index - 1]
+            range: [pos, index]
         });
 
         return regex;
@@ -3259,34 +3879,21 @@ parseStatement: true, parseSourceElement: true */
             }
 
             function visit(node) {
-                if (range) {
-                    if (isBinary(node.left) && (typeof node.left.range === 'undefined')) {
-                        visit(node.left);
-                    }
-                    if (isBinary(node.right) && (typeof node.right.range === 'undefined')) {
-                        visit(node.right);
-                    }
-
-                    // Expression enclosed in brackets () already has the correct range.
-                    if (typeof node.range === 'undefined') {
-                        node.range = [node.left.range[0], node.right.range[1]];
-                    }
+                if (isBinary(node.left)) {
+                    visit(node.left);
+                }
+                if (isBinary(node.right)) {
+                    visit(node.right);
                 }
 
-                if (loc) {
-                    if (isBinary(node.left) && (typeof node.left.loc === 'undefined')) {
-                        visit(node.left);
-                    }
-                    if (isBinary(node.right) && (typeof node.right.loc === 'undefined')) {
-                        visit(node.right);
-                    }
-
-                    if (typeof node.loc === 'undefined') {
-                        node.loc = {
-                            start: node.left.loc.start,
-                            end: node.right.loc.end
-                        };
-                    }
+                if (range && typeof node.range === 'undefined') {
+                    node.range = [node.left.range[0], node.right.range[1]];
+                }
+                if (loc && typeof node.loc === 'undefined') {
+                    node.loc = {
+                        start: node.left.loc.start,
+                        end: node.right.loc.end
+                    };
                 }
             }
 
@@ -3306,7 +3913,7 @@ parseStatement: true, parseSourceElement: true */
                 if (typeof node !== 'undefined') {
 
                     if (range) {
-                        rangeInfo[1] = index - 1;
+                        rangeInfo[1] = index;
                         node.range = rangeInfo;
                     }
 
@@ -3368,26 +3975,37 @@ parseStatement: true, parseSourceElement: true */
             extra.parseConditionalExpression = parseConditionalExpression;
             extra.parseConstLetDeclaration = parseConstLetDeclaration;
             extra.parseEqualityExpression = parseEqualityExpression;
+            extra.parseExportDeclaration = parseExportDeclaration;
+            extra.parseExportSpecifier = parseExportSpecifier;
+            extra.parseExportSpecifierSetProperty = parseExportSpecifierSetProperty;
             extra.parseExpression = parseExpression;
             extra.parseForVariableDeclaration = parseForVariableDeclaration;
             extra.parseFunctionDeclaration = parseFunctionDeclaration;
             extra.parseFunctionExpression = parseFunctionExpression;
+            extra.parseGlob = parseGlob;
+            extra.parseImportDeclaration = parseImportDeclaration;
+            extra.parseImportSpecifier = parseImportSpecifier;
             extra.parseLogicalANDExpression = parseLogicalANDExpression;
             extra.parseLogicalORExpression = parseLogicalORExpression;
             extra.parseMultiplicativeExpression = parseMultiplicativeExpression;
+            extra.parseModuleDeclaration = parseModuleDeclaration;
+            extra.parseModuleBlock = parseModuleBlock;
             extra.parseNewExpression = parseNewExpression;
             extra.parseNonComputedMember = parseNonComputedMember;
             extra.parseNonComputedProperty = parseNonComputedProperty;
             extra.parseObjectProperty = parseObjectProperty;
             extra.parseObjectPropertyKey = parseObjectPropertyKey;
+            extra.parsePath = parsePath;
             extra.parsePostfixExpression = parsePostfixExpression;
             extra.parsePrimaryExpression = parsePrimaryExpression;
             extra.parseProgram = parseProgram;
             extra.parsePropertyFunction = parsePropertyFunction;
+            extra.parseProtoExpression = parseProtoExpression;
             extra.parseRelationalExpression = parseRelationalExpression;
             extra.parseStatement = parseStatement;
             extra.parseShiftExpression = parseShiftExpression;
             extra.parseSwitchCase = parseSwitchCase;
+            extra.parseTriangleLiteral = parseTriangleLiteral;
             extra.parseUnaryExpression = parseUnaryExpression;
             extra.parseVariableDeclaration = parseVariableDeclaration;
             extra.parseVariableIdentifier = parseVariableIdentifier;
@@ -3404,27 +4022,38 @@ parseStatement: true, parseSourceElement: true */
             parseComputedMember = wrapTracking(extra.parseComputedMember);
             parseConditionalExpression = wrapTracking(extra.parseConditionalExpression);
             parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration);
+            parseExportDeclaration = wrapTracking(parseExportDeclaration);
+            parseExportSpecifier = wrapTracking(parseExportSpecifier);
+            parseExportSpecifierSetProperty = wrapTracking(parseExportSpecifierSetProperty);
             parseEqualityExpression = wrapTracking(extra.parseEqualityExpression);
             parseExpression = wrapTracking(extra.parseExpression);
             parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration);
             parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration);
             parseFunctionExpression = wrapTracking(extra.parseFunctionExpression);
+            parseGlob = wrapTracking(extra.parseGlob);
+            parseImportDeclaration = wrapTracking(extra.parseImportDeclaration);
+            parseImportSpecifier = wrapTracking(extra.parseImportSpecifier);
             parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression);
             parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression);
             parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression);
+            parseModuleDeclaration = wrapTracking(extra.parseModuleDeclaration);
+            parseModuleBlock = wrapTracking(extra.parseModuleBlock);
             parseNewExpression = wrapTracking(extra.parseNewExpression);
             parseNonComputedMember = wrapTracking(extra.parseNonComputedMember);
             parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty);
             parseObjectProperty = wrapTracking(extra.parseObjectProperty);
             parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey);
+            parsePath = wrapTracking(extra.parsePath);
             parsePostfixExpression = wrapTracking(extra.parsePostfixExpression);
             parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression);
             parseProgram = wrapTracking(extra.parseProgram);
             parsePropertyFunction = wrapTracking(extra.parsePropertyFunction);
+            parseProtoExpression = wrapTracking(parseProtoExpression);
             parseRelationalExpression = wrapTracking(extra.parseRelationalExpression);
             parseStatement = wrapTracking(extra.parseStatement);
             parseShiftExpression = wrapTracking(extra.parseShiftExpression);
             parseSwitchCase = wrapTracking(extra.parseSwitchCase);
+            parseTriangleLiteral = wrapTracking(extra.parseTriangleLiteral);
             parseUnaryExpression = wrapTracking(extra.parseUnaryExpression);
             parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration);
             parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier);
@@ -3462,26 +4091,37 @@ parseStatement: true, parseSourceElement: true */
             parseConditionalExpression = extra.parseConditionalExpression;
             parseConstLetDeclaration = extra.parseConstLetDeclaration;
             parseEqualityExpression = extra.parseEqualityExpression;
+            parseExportDeclaration = extra.parseExportDeclaration;
+            parseExportSpecifier = extra.parseExportSpecifier;
+            parseExportSpecifierSetProperty = extra.parseExportSpecifierSetProperty;
             parseExpression = extra.parseExpression;
             parseForVariableDeclaration = extra.parseForVariableDeclaration;
             parseFunctionDeclaration = extra.parseFunctionDeclaration;
             parseFunctionExpression = extra.parseFunctionExpression;
+            parseGlob = extra.parseGlob;
+            parseImportDeclaration = extra.parseImportDeclaration;
+            parseImportSpecifier = extra.parseImportSpecifier;
             parseLogicalANDExpression = extra.parseLogicalANDExpression;
             parseLogicalORExpression = extra.parseLogicalORExpression;
             parseMultiplicativeExpression = extra.parseMultiplicativeExpression;
+            parseModuleDeclaration = extra.parseModuleDeclaration;
+            parseModuleBlock = extra.parseModuleBlock;
             parseNewExpression = extra.parseNewExpression;
             parseNonComputedMember = extra.parseNonComputedMember;
             parseNonComputedProperty = extra.parseNonComputedProperty;
             parseObjectProperty = extra.parseObjectProperty;
             parseObjectPropertyKey = extra.parseObjectPropertyKey;
-            parsePrimaryExpression = extra.parsePrimaryExpression;
+            parsePath = extra.parsePath;
             parsePostfixExpression = extra.parsePostfixExpression;
+            parsePrimaryExpression = extra.parsePrimaryExpression;
             parseProgram = extra.parseProgram;
             parsePropertyFunction = extra.parsePropertyFunction;
+            parseProtoExpression = extra.parseProtoExpression;
             parseRelationalExpression = extra.parseRelationalExpression;
             parseStatement = extra.parseStatement;
             parseShiftExpression = extra.parseShiftExpression;
             parseSwitchCase = extra.parseSwitchCase;
+            parseTriangleLiteral = extra.parseTriangleLiteral;
             parseUnaryExpression = extra.parseUnaryExpression;
             parseVariableDeclaration = extra.parseVariableDeclaration;
             parseVariableIdentifier = extra.parseVariableIdentifier;
@@ -3497,7 +4137,7 @@ parseStatement: true, parseSourceElement: true */
         var length = str.length,
             result = [],
             i;
-        for (i = 0; i < length; i += 1) {
+        for (i = 0; i < length; ++i) {
             result[i] = str.charAt(i);
         }
         return result;
@@ -3517,12 +4157,14 @@ parseStatement: true, parseSourceElement: true */
         lineStart = 0;
         length = source.length;
         buffer = null;
-        allowIn = true;
-        labelSet = {};
-        inSwitch = false;
-        inIteration = false;
-        lastParenthesized = null;
-        inFunctionBody = false;
+        state = {
+            allowIn: true,
+            labelSet: {},
+            lastParenthesized: null,
+            inFunctionBody: false,
+            inIteration: false,
+            inSwitch: false
+        };
 
         extra = {};
         if (typeof options !== 'undefined') {
@@ -3535,6 +4177,9 @@ parseStatement: true, parseSourceElement: true */
             if (typeof options.comment === 'boolean' && options.comment) {
                 extra.comments = [];
             }
+            if (typeof options.tolerant === 'boolean' && options.tolerant) {
+                extra.errors = [];
+            }
         }
 
         if (length > 0) {
@@ -3562,6 +4207,9 @@ parseStatement: true, parseSourceElement: true */
             if (typeof extra.tokens !== 'undefined') {
                 program.tokens = extra.tokens;
             }
+            if (typeof extra.errors !== 'undefined') {
+                program.errors = extra.errors;
+            }
         } catch (e) {
             throw e;
         } finally {
@@ -3572,173 +4220,31 @@ parseStatement: true, parseSourceElement: true */
         return program;
     }
 
-    // Executes visitor on the object and its children (recursively).
+    // Sync with package.json.
+    exports.version = '1.0.0-dev';
 
-    function traverse(object, visitor, master) {
-        var key, child, parent, path;
+    exports.parse = parse;
 
-        parent = (typeof master === 'undefined') ? [] : master;
+    // Deep copy.
+    exports.Syntax = (function () {
+        var name, types = {};
 
-        if (visitor.call(null, object, parent) === false) {
-            return;
+        if (typeof Object.create === 'function') {
+            types = Object.create(null);
         }
-        for (key in object) {
-            if (object.hasOwnProperty(key)) {
-                child = object[key];
-                path = [ object ];
-                path.push(parent);
-                if (typeof child === 'object' && child !== null) {
-                    traverse(child, visitor, path);
-                }
-            }
-        }
-    }
-
-    // Insert a prolog in the body of every function.
-    // It will be in the form of a function call:
-    //
-    //     traceName(object);
-    //
-    // where the object contains the following properties:
-    //
-    //    'name' holds the name of the function
-    //    'lineNumber' holds the starting line number of the function block
-    //    'range' contains the index-based range of the function
-    //
-    // The name of the function represents the associated reference for
-    // the function (deduced on a best-effort basis if it is not
-    // a function declaration).
-    //
-    // If traceName is a function instead of a string, it will be invoked and
-    // the result will be used as the entire prolog. The arguments for the
-    // invocation are the function name, range, and location info.
-
-    function traceFunctionEntrance(traceName) {
-
-        return function (code) {
-            var tree,
-                functionList,
-                param,
-                signature,
-                pos,
-                i;
-
-
-            tree = parse(code, { range: true, loc: true });
-
-            functionList = [];
-            traverse(tree, function (node, path) {
-                var parent;
-                if (node.type === Syntax.FunctionDeclaration) {
-                    functionList.push({
-                        name: node.id.name,
-                        range: node.range,
-                        loc: node.loc,
-                        blockStart: node.body.range[0]
-                    });
-                } else if (node.type === Syntax.FunctionExpression) {
-                    parent = path[0];
-                    if (parent.type === Syntax.AssignmentExpression) {
-                        if (typeof parent.left.range !== 'undefined') {
-                            functionList.push({
-                                name: code.slice(parent.left.range[0],
-                                          parent.left.range[1] + 1),
-                                range: node.range,
-                                loc: node.loc,
-                                blockStart: node.body.range[0]
-                            });
-                        }
-                    } else if (parent.type === Syntax.VariableDeclarator) {
-                        functionList.push({
-                            name: parent.id.name,
-                            range: node.range,
-                            loc: node.loc,
-                            blockStart: node.body.range[0]
-                        });
-                    } else if (parent.type === Syntax.CallExpression) {
-                        functionList.push({
-                            name: parent.id ? parent.id.name : '[Anonymous]',
-                            range: node.range,
-                            loc: node.loc,
-                            blockStart: node.body.range[0]
-                        });
-                    } else if (typeof parent.length === 'number') {
-                        functionList.push({
-                            name: parent.id ? parent.id.name : '[Anonymous]',
-                            range: node.range,
-                            loc: node.loc,
-                            blockStart: node.body.range[0]
-                        });
-                    } else if (typeof parent.key !== 'undefined') {
-                        if (parent.key.type === 'Identifier') {
-                            if (parent.value === node && parent.key.name) {
-                                functionList.push({
-                                    name: parent.key.name,
-                                    range: node.range,
-                                    loc: node.loc,
-                                    blockStart: node.body.range[0]
-                                });
-                            }
-                        }
-                    }
-                }
-            });
-
-            // Insert the instrumentation code from the last entry.
-            // This is to ensure that the range for each entry remains valid)
-            // (it won't shift due to some new inserting string before the range).
-            for (i = functionList.length - 1; i >= 0; i -= 1) {
-                param = {
-                    name: functionList[i].name,
-                    range: functionList[i].range,
-                    loc: functionList[i].loc
-                };
-                if (typeof traceName === 'function') {
-                    signature = traceName.call(null, param);
-                } else {
-                    signature = traceName + '({ ';
-                    signature += 'name: \'' + functionList[i].name + '\', ';
-                    if (typeof functionList[i].loc !== 'undefined') {
-                        signature += 'lineNumber: ' + functionList[i].loc.start.line + ', ';
-                    }
-                    signature += 'range: [' + functionList[i].range[0] + ', ' +
-                        functionList[i].range[1] + '] ';
-                    signature += '});';
-                }
-                pos = functionList[i].blockStart + 1;
-                code = code.slice(0, pos) + '\n' + signature + code.slice(pos, code.length);
-            }
 
-            return code;
-        };
-    }
-
-    function modify(code, modifiers) {
-        var i;
-
-        if (Object.prototype.toString.call(modifiers) === '[object Array]') {
-            for (i = 0; i < modifiers.length; i += 1) {
-                code = modifiers[i].call(null, code);
+        for (name in Syntax) {
+            if (Syntax.hasOwnProperty(name)) {
+                types[name] = Syntax[name];
             }
-        } else if (typeof modifiers === 'function') {
-            code = modifiers.call(null, code);
-        } else {
-            throw new Error('Wrong use of esprima.modify() function');
         }
 
-        return code;
-    }
-
-    // Sync with package.json.
-    exports.version = '0.9.9-dev';
-
-    exports.parse = parse;
-
-    exports.modify = modify;
+        if (typeof Object.freeze === 'function') {
+            Object.freeze(types);
+        }
 
-    exports.Tracer = {
-        FunctionEntrance: traceFunctionEntrance
-    };
+        return types;
+    }());
 
 }(typeof exports === 'undefined' ? (esprima = {}) : exports));
 /* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/escodegen.js b/escodegen.js
index 0590190..59087f6 100644
--- a/escodegen.js
+++ b/escodegen.js
@@ -1,4 +1,5 @@
 /*
+  Copyright (C) 2012 John Freeman <jf...@gmail.com>
   Copyright (C) 2012 Ariya Hidayat <ar...@gmail.com>
   Copyright (C) 2012 Mathias Bynens <ma...@qiwi.be>
   Copyright (C) 2012 Joost-Wim Boekesteijn <jo...@boekesteijn.nl>
@@ -37,6 +38,9 @@
     var Syntax,
         Precedence,
         BinaryPrecedence,
+        VisitorKeys,
+        VisitorOption,
+        isArray,
         base,
         indent,
         extra,
@@ -139,10 +143,12 @@
             indent: null,
             base: null,
             parse: null,
+            comment: false,
             format: {
                 indent: {
                     style: '    ',
-                    base: 0
+                    base: 0,
+                    adjustMultilineComment: false
                 }
             }
         };
@@ -179,6 +185,42 @@
         return result;
     }
 
+    isArray = Array.isArray;
+    if (!isArray) {
+        isArray = function isArray(array) {
+            return Object.prototype.toString.call(array) === '[object Array]';
+        };
+    }
+
+    function endsWithLineTerminator(value) {
+        return (/(?:\r\n|[\n\r])$/).test(value);
+    }
+
+    function shallowCopy(obj) {
+        var ret = {}, key;
+        for (key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                ret[key] = obj[key];
+            }
+        }
+        return ret;
+    }
+
+    function deepCopy(obj) {
+        var ret = {}, key, val;
+        for (key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                val = obj[key];
+                if (typeof val === 'object' && val !== null) {
+                    ret[key] = deepCopy(val);
+                } else {
+                    ret[key] = val;
+                }
+            }
+        }
+        return ret;
+    }
+
     function updateDeeply(target, override) {
         var key, val;
 
@@ -247,18 +289,71 @@
         return '\'' + result + '\'';
     }
 
+    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);
+    }
+
     function addIndent(stmt) {
         return base + stmt;
     }
 
+    function adjustMultilineComment(value) {
+        var array, i, len, line, j, ch, spaces;
+
+        spaces = Number.MAX_VALUE;
+        array = value.split(/\r\n|[\r\n]/);
+
+        // first line doesn't have indentation
+        for (i = 1, len = array.length; i < len; i += 1) {
+            line = array[i];
+            j = 0;
+            while (j < line.length && isWhiteSpace(line[j])) {
+                j += 1;
+            }
+            if (spaces > j) {
+                spaces = j;
+            }
+        }
+
+        if (spaces % 2 === 1) {
+            // /*
+            //  *
+            //  */
+            // If spaces are odd number, above pattern is considered.
+            // We waste 1 space.
+            spaces -= 1;
+        }
+        for (i = 1, len = array.length; i < len; i += 1) {
+            array[i] = addIndent(array[i].slice(spaces));
+        }
+        return array.join('\n');
+    }
+
+    function generateComment(comment) {
+        if (comment.type === 'Line') {
+            // Esprima always produce last line comment with LineTerminator
+            return '//' + comment.value;
+        }
+        if (extra.format.indent.adjustMultilineComment && /[\n\r]/.test(comment.value)) {
+            return adjustMultilineComment('/*' + comment.value + '*/');
+        }
+        return '/*' + comment.value + '*/';
+    }
+
     function parenthesize(text, current, should) {
-        return (current < should) ?  '(' + text + ')' : text;
+        if (current < should) {
+            return '(' + text + ')';
+        }
+        return text;
     }
 
     function maybeBlock(stmt, suffix) {
         var previousBase, result;
 
-        if (stmt.type === Syntax.BlockStatement) {
+        if (stmt.type === Syntax.BlockStatement && (!extra.comment || !stmt.leadingComments)) {
             result = ' ' + generateStatement(stmt);
             if (suffix) {
                 return result + ' ';
@@ -266,7 +361,7 @@
             return result;
         }
 
-        if (stmt.type === Syntax.EmptyStatement) {
+        if (stmt.type === Syntax.EmptyStatement && (!extra.comment || !stmt.leadingComments)) {
             result = ';';
         } else {
             previousBase = base;
@@ -293,18 +388,23 @@
         return result + ')' + maybeBlock(node.body);
     }
 
-    function generateExpression(expr, precedence) {
-        var result, currentPrecedence, previousBase, i, len, raw;
+    function generateExpression(expr, option) {
+        var result, precedence, currentPrecedence, previousBase, i, len, raw, allowIn, allowCall;
 
-        if (!precedence) {
-            precedence = Precedence.Sequence;
-        }
+        precedence = option.precedence;
+        allowIn = option.allowIn;
+        allowCall = option.allowCall;
 
         switch (expr.type) {
         case Syntax.SequenceExpression:
             result = '';
+            allowIn |= (Precedence.Sequence < precedence);
             for (i = 0, len = expr.expressions.length; i < len; i += 1) {
-                result += generateExpression(expr.expressions[i], Precedence.Assignment);
+                result += generateExpression(expr.expressions[i], {
+                    precedence: Precedence.Assignment,
+                    allowIn: allowIn,
+                    allowCall: true
+                });
                 if ((i + 1) < len) {
                     result += ', ';
                 }
@@ -313,19 +413,41 @@
             break;
 
         case Syntax.AssignmentExpression:
+            allowIn |= (Precedence.Assignment < precedence);
             result = parenthesize(
-                generateExpression(expr.left, Precedence.Call) + ' ' + expr.operator + ' ' +
-                    generateExpression(expr.right, Precedence.Assignment),
+                generateExpression(expr.left, {
+                    precedence: Precedence.Call,
+                    allowIn: allowIn,
+                    allowCall: true
+                }) + ' ' + expr.operator + ' ' +
+                    generateExpression(expr.right, {
+                        precedence: Precedence.Assignment,
+                        allowIn: allowIn,
+                        allowCall: true
+                    }),
                 Precedence.Assignment,
                 precedence
             );
             break;
 
         case Syntax.ConditionalExpression:
+            allowIn |= (Precedence.Conditional < precedence);
             result = parenthesize(
-                generateExpression(expr.test, Precedence.LogicalOR) + ' ? ' +
-                    generateExpression(expr.consequent, Precedence.Assignment) + ' : ' +
-                    generateExpression(expr.alternate, Precedence.Assignment),
+                generateExpression(expr.test, {
+                    precedence: Precedence.LogicalOR,
+                    allowIn: allowIn,
+                    allowCall: true
+                }) + ' ? ' +
+                    generateExpression(expr.consequent, {
+                        precedence: Precedence.Assignment,
+                        allowIn: allowIn,
+                        allowCall: true
+                    }) + ' : ' +
+                    generateExpression(expr.alternate, {
+                        precedence: Precedence.Assignment,
+                        allowIn: allowIn,
+                        allowCall: true
+                    }),
                 Precedence.Conditional,
                 precedence
             );
@@ -335,51 +457,91 @@
         case Syntax.BinaryExpression:
             currentPrecedence = BinaryPrecedence[expr.operator];
 
-            result = generateExpression(expr.left, currentPrecedence) +
-                ' ' + expr.operator + ' ' +
-                generateExpression(expr.right, currentPrecedence + 1);
-            if (expr.operator === 'in') {
-                // TODO parenthesize only in allowIn = false case
+            allowIn |= (currentPrecedence < precedence);
+
+            result =
+                generateExpression(expr.left, {
+                    precedence: currentPrecedence,
+                    allowIn: allowIn,
+                    allowCall: true
+                }) + ' ' + expr.operator + ' ' +
+                generateExpression(expr.right, {
+                    precedence: currentPrecedence + 1,
+                    allowIn: allowIn,
+                    allowCall: true
+                });
+
+            if (expr.operator === 'in' && !allowIn) {
                 result = '(' + result + ')';
             } else {
                 result = parenthesize(result, currentPrecedence, precedence);
             }
+
             break;
 
         case Syntax.CallExpression:
-            result = '';
+            result = generateExpression(expr.callee, {
+                precedence: Precedence.Call,
+                allowIn: true,
+                allowCall: true
+            });
+
+            result += '(';
             for (i = 0, len = expr['arguments'].length; i < len; i += 1) {
-                result += generateExpression(expr['arguments'][i], Precedence.Assignment);
+                result += generateExpression(expr['arguments'][i], {
+                    precedence: Precedence.Assignment,
+                    allowIn: true,
+                    allowCall: true
+                });
                 if ((i + 1) < len) {
                     result += ', ';
                 }
             }
-            result = parenthesize(
-                generateExpression(expr.callee, Precedence.Call) + '(' + result + ')',
-                Precedence.Call,
-                precedence
-            );
+            result += ')';
+
+            if (!allowCall) {
+                result = '(' + result + ')';
+            } else {
+                result = parenthesize(result, Precedence.Call, precedence);
+            }
             break;
 
         case Syntax.NewExpression:
-            result = '';
+            result = 'new ' + generateExpression(expr.callee, {
+                precedence: Precedence.New,
+                allowIn: true,
+                allowCall: false
+            });
+
+            result += '(';
             for (i = 0, len = expr['arguments'].length; i < len; i += 1) {
-                result += generateExpression(expr['arguments'][i], Precedence.Assignment);
+                result += generateExpression(expr['arguments'][i], {
+                    precedence: Precedence.Assignment,
+                    allowIn: true,
+                    allowCall: true
+                });
                 if ((i + 1) < len) {
                     result += ', ';
                 }
             }
-            result = parenthesize(
-                'new ' + generateExpression(expr.callee, Precedence.New) + '(' + result + ')',
-                Precedence.New,
-                precedence
-            );
+            result += ')';
+
+            result = parenthesize(result, Precedence.New, precedence);
             break;
 
         case Syntax.MemberExpression:
-            result = generateExpression(expr.object, Precedence.Call);
+            result = generateExpression(expr.object, {
+                precedence: Precedence.Call,
+                allowIn: true,
+                allowCall: allowCall
+            });
+
             if (expr.computed) {
-                result += '[' + generateExpression(expr.property) + ']';
+                result += '[' + 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) {
@@ -390,6 +552,7 @@
                 }
                 result += '.' + expr.property.name;
             }
+
             result = parenthesize(result, Precedence.Member, precedence);
             break;
 
@@ -399,13 +562,15 @@
                 result += ' ';
             }
             result = parenthesize(
-                result + generateExpression(expr.argument, Precedence.Unary +
-                    (
+                result + generateExpression(expr.argument, {
+                    precedence: Precedence.Unary + (
                         expr.argument.type === Syntax.UnaryExpression &&
-                        expr.operator.length < 3 &&
-                        expr.argument.operator === expr.operator ? 1 : 0
-                    )
+                            expr.operator.length < 3 &&
+                            expr.argument.operator === expr.operator ? 1 : 0
                     ),
+                    allowIn: true,
+                    allowCall: true
+                }),
                 Precedence.Unary,
                 precedence
             );
@@ -415,14 +580,21 @@
             if (expr.prefix) {
                 result = parenthesize(
                     expr.operator +
-                        generateExpression(expr.argument, Precedence.Unary),
+                        generateExpression(expr.argument, {
+                            precedence: Precedence.Unary,
+                            allowIn: true,
+                            allowCall: true
+                        }),
                     Precedence.Unary,
                     precedence
                 );
             } else {
                 result = parenthesize(
-                    generateExpression(expr.argument, Precedence.Postfix) +
-                        expr.operator,
+                    generateExpression(expr.argument, {
+                        precedence: Precedence.Postfix,
+                        allowIn: true,
+                        allowCall: true
+                    }) + expr.operator,
                     Precedence.Postfix,
                     precedence
                 );
@@ -452,7 +624,11 @@
                         result += ',';
                     }
                 } else {
-                    result += addIndent(generateExpression(expr.elements[i], Precedence.Assignment));
+                    result += addIndent(generateExpression(expr.elements[i], {
+                        precedence: Precedence.Assignment,
+                        allowIn: true,
+                        allowCall: true
+                    }));
                 }
                 if ((i + 1) < len) {
                     result += ',\n';
@@ -464,11 +640,22 @@
 
         case Syntax.Property:
             if (expr.kind === 'get' || expr.kind === 'set') {
-                result = expr.kind + ' ' + generateExpression(expr.key) +
-                    generateFunctionBody(expr.value);
+                result = expr.kind + ' ' + generateExpression(expr.key, {
+                    precedence: Precedence.Sequence,
+                    allowIn: true,
+                    allowCall: true
+                }) + generateFunctionBody(expr.value);
             } else {
-                result = generateExpression(expr.key) + ': ' +
-                    generateExpression(expr.value, Precedence.Assignment);
+                result =
+                    generateExpression(expr.key, {
+                        precedence: Precedence.Sequence,
+                        allowIn: true,
+                        allowCall: true
+                    }) + ': ' + generateExpression(expr.value, {
+                        precedence: Precedence.Assignment,
+                        allowIn: true,
+                        allowCall: true
+                    });
             }
             break;
 
@@ -481,7 +668,11 @@
             previousBase = base;
             base += indent;
             for (i = 0, len = expr.properties.length; i < len; i += 1) {
-                result += addIndent(generateExpression(expr.properties[i]));
+                result += addIndent(generateExpression(expr.properties[i], {
+                    precedence: Precedence.Sequence,
+                    allowIn: true,
+                    allowCall: true
+                }));
                 if ((i + 1) < len) {
                     result += ',\n';
                 }
@@ -542,8 +733,13 @@
         return result;
     }
 
-    function generateStatement(stmt) {
-        var i, len, result, previousBase;
+    function generateStatement(stmt, option) {
+        var i, len, result, previousBase, comment, save, ret, node, allowIn;
+
+        allowIn = true;
+        if (option) {
+            allowIn = option.allowIn;
+        }
 
         switch (stmt.type) {
         case Syntax.BlockStatement:
@@ -576,13 +772,21 @@
             break;
 
         case Syntax.DoWhileStatement:
-            result = 'do' + maybeBlock(stmt.body, true) + 'while (' + generateExpression(stmt.test) + ');';
+            result = 'do' + maybeBlock(stmt.body, true) + 'while (' + generateExpression(stmt.test, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            }) + ');';
             break;
 
         case Syntax.CatchClause:
             previousBase = base;
             base += indent;
-            result = ' catch (' + generateExpression(stmt.param) + ')';
+            result = ' catch (' + generateExpression(stmt.param, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            }) + ')';
             base = previousBase;
             result += maybeBlock(stmt.body);
             break;
@@ -596,7 +800,11 @@
             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) {
@@ -608,27 +816,54 @@
 
         case Syntax.VariableDeclarator:
             if (stmt.init) {
-                result = stmt.id.name + ' = ' + generateExpression(stmt.init, Precedence.Assignment);
+                result = stmt.id.name + ' = ' + 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 += ' ' + 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;
-                for (i = 0, len = stmt.declarations.length; i < len; i += 1) {
-                    result += generateStatement(stmt.declarations[i]);
-                    if ((i + 1) < len) {
-                        result += ', ';
+
+                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];
+                    if (extra.comment && node.leadingComments) {
+                        result += ',\n' + addIndent(generateStatement(node, {
+                            allowIn: allowIn
+                        }));
+                    } else {
+                        result += ', ' + generateStatement(node, {
+                            allowIn: allowIn
+                        });
                     }
                 }
                 base = previousBase;
@@ -637,7 +872,11 @@
             break;
 
         case Syntax.ThrowStatement:
-            result = 'throw ' + generateExpression(stmt.argument) + ';';
+            result = 'throw ' + generateExpression(stmt.argument, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            }) + ';';
             break;
 
         case Syntax.TryStatement:
@@ -653,7 +892,11 @@
         case Syntax.SwitchStatement:
             previousBase = base;
             base += indent;
-            result = 'switch (' + generateExpression(stmt.discriminant) + ') {\n';
+            result = 'switch (' + generateExpression(stmt.discriminant, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            }) + ') {\n';
             base = previousBase;
             if (stmt.cases) {
                 for (i = 0, len = stmt.cases.length; i < len; i += 1) {
@@ -667,7 +910,11 @@
             previousBase = base;
             base += indent;
             if (stmt.test) {
-                result = 'case ' + generateExpression(stmt.test) + ':';
+                result = 'case ' + generateExpression(stmt.test, {
+                    precedence: Precedence.Sequence,
+                    allowIn: true,
+                    allowCall: true
+                }) + ':';
             } else {
                 result = 'default:';
             }
@@ -691,20 +938,32 @@
                 if (stmt.alternate.type === Syntax.IfStatement) {
                     previousBase = base;
                     base += indent;
-                    result = 'if (' +  generateExpression(stmt.test) + ')';
+                    result = 'if (' +  generateExpression(stmt.test, {
+                        precedence: Precedence.Sequence,
+                        allowIn: true,
+                        allowCall: true
+                    }) + ')';
                     base = previousBase;
                     result += maybeBlock(stmt.consequent, true) + 'else ' + generateStatement(stmt.alternate);
                 } else {
                     previousBase = base;
                     base += indent;
-                    result = 'if (' + generateExpression(stmt.test) + ')';
+                    result = 'if (' + generateExpression(stmt.test, {
+                        precedence: Precedence.Sequence,
+                        allowIn: true,
+                        allowCall: true
+                    }) + ')';
                     base = previousBase;
                     result += maybeBlock(stmt.consequent, true) + 'else' + maybeBlock(stmt.alternate);
                 }
             } else {
                 previousBase = base;
                 base += indent;
-                result = 'if (' + generateExpression(stmt.test) + ')';
+                result = 'if (' + generateExpression(stmt.test, {
+                    precedence: Precedence.Sequence,
+                    allowIn: true,
+                    allowCall: true
+                }) + ')';
                 base = previousBase;
                 result += maybeBlock(stmt.consequent);
             }
@@ -716,22 +975,36 @@
             result = 'for (';
             if (stmt.init) {
                 if (stmt.init.type === Syntax.VariableDeclaration) {
-                    result += generateStatement(stmt.init);
+                    result += generateStatement(stmt.init, {
+                        allowIn: false
+                    });
                 } else {
-                    result += generateExpression(stmt.init) + ';';
+                    result += generateExpression(stmt.init, {
+                        precedence: Precedence.Sequence,
+                        allowIn: false,
+                        allowCall: true
+                    }) + ';';
                 }
             } else {
                 result += ';';
             }
 
             if (stmt.test) {
-                result += ' ' + generateExpression(stmt.test) + ';';
+                result += ' ' + generateExpression(stmt.test, {
+                    precedence: Precedence.Sequence,
+                    allowIn: true,
+                    allowCall: true
+                }) + ';';
             } else {
                 result += ';';
             }
 
             if (stmt.update) {
-                result += ' ' + generateExpression(stmt.update) + ')';
+                result += ' ' + generateExpression(stmt.update, {
+                    precedence: Precedence.Sequence,
+                    allowIn: true,
+                    allowCall: true
+                }) + ')';
             } else {
                 result += ')';
             }
@@ -745,18 +1018,28 @@
             if (stmt.left.type === Syntax.VariableDeclaration) {
                 previousBase = base;
                 base += indent + indent;
-                result += stmt.left.kind + ' ' + generateStatement(stmt.left.declarations[0]);
+                result += stmt.left.kind + ' ' + generateStatement(stmt.left.declarations[0], {
+                    allowIn: false
+                });
                 base = previousBase;
             } else {
                 previousBase = base;
                 base += indent;
-                result += generateExpression(stmt.left, Precedence.Call);
+                result += generateExpression(stmt.left, {
+                    precedence: Precedence.Call,
+                    allowIn: true,
+                    allowCall: true
+                });
                 base = previousBase;
             }
 
             previousBase = base;
             base += indent;
-            result += ' in ' + generateExpression(stmt.right) + ')';
+            result += ' in ' + generateExpression(stmt.right, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            }) + ')';
             base = previousBase;
             result += maybeBlock(stmt.body);
             break;
@@ -785,7 +1068,11 @@
 
         case Syntax.ReturnStatement:
             if (stmt.argument) {
-                result = 'return ' + generateExpression(stmt.argument) + ';';
+                result = 'return ' + generateExpression(stmt.argument, {
+                    precedence: Precedence.Sequence,
+                    allowIn: true,
+                    allowCall: true
+                }) + ';';
             } else {
                 result = 'return;';
             }
@@ -794,7 +1081,11 @@
         case Syntax.WhileStatement:
             previousBase = base;
             base += indent;
-            result = 'while (' + generateExpression(stmt.test) + ')';
+            result = 'while (' + generateExpression(stmt.test, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            }) + ')';
             base = previousBase;
             result += maybeBlock(stmt.body);
             break;
@@ -802,7 +1093,11 @@
         case Syntax.WithStatement:
             previousBase = base;
             base += indent;
-            result = 'with (' + generateExpression(stmt.object) + ')';
+            result = 'with (' + generateExpression(stmt.object, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            }) + ')';
             base = previousBase;
             result += maybeBlock(stmt.body);
             break;
@@ -814,6 +1109,42 @@
         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);
+            }
+
+            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));
+                }
+            }
+        }
+
         return result;
     }
 
@@ -845,6 +1176,7 @@
             base = stringRepeat(indent, options.format.indent.base);
             parse = options.parse;
         }
+        extra = options;
 
         switch (node.type) {
         case Syntax.BlockStatement:
@@ -889,7 +1221,11 @@
         case Syntax.ThisExpression:
         case Syntax.UnaryExpression:
         case Syntax.UpdateExpression:
-            return generateExpression(node);
+            return generateExpression(node, {
+                precedence: Precedence.Sequence,
+                allowIn: true,
+                allowCall: true
+            });
 
         default:
             break;
@@ -897,10 +1233,295 @@
         throw new Error('Unknown node type: ' + node.type);
     }
 
+    // simple visitor implementation
+
+    VisitorKeys = {
+        AssignmentExpression: ['left', 'right'],
+        ArrayExpression: ['elements'],
+        BlockStatement: ['body'],
+        BinaryExpression: ['left', 'right'],
+        BreakStatement: ['label'],
+        CallExpression: ['callee', 'arguments'],
+        CatchClause: ['param', 'body'],
+        ConditionalExpression: ['test', 'consequent', 'alternate'],
+        ContinueStatement: ['label'],
+        DoWhileStatement: ['body', 'test'],
+        DebuggerStatement: [],
+        EmptyStatement: [],
+        ExpressionStatement: ['expression'],
+        ForStatement: ['init', 'test', 'update', 'body'],
+        ForInStatement: ['left', 'right', 'body'],
+        FunctionDeclaration: ['id', 'params', 'body'],
+        FunctionExpression: ['id', 'params', 'body'],
+        Identifier: [],
+        IfStatement: ['test', 'consequent', 'alternate'],
+        Literal: [],
+        LabeledStatement: ['label', 'body'],
+        LogicalExpression: ['left', 'right'],
+        MemberExpression: ['object', 'property'],
+        NewExpression: ['callee', 'arguments'],
+        ObjectExpression: ['properties'],
+        Program: ['body'],
+        Property: ['key', 'value'],
+        ReturnStatement: ['argument'],
+        SequenceExpression: ['expressions'],
+        SwitchStatement: ['descriminant', 'cases'],
+        SwitchCase: ['test', 'consequent'],
+        ThisExpression: [],
+        ThrowStatement: ['argument'],
+        TryStatement: ['block', 'handlers', 'finalizer'],
+        UnaryExpression: ['argument'],
+        UpdateExpression: ['argument'],
+        VariableDeclaration: ['declarations'],
+        VariableDeclarator: ['id', 'init'],
+        WhileStatement: ['test', 'body'],
+        WithStatement: ['object', 'body']
+    };
+
+    VisitorOption = {
+        Break: 1,
+        Skip: 2
+    };
+
+    function traverse(top, visitor) {
+        var worklist, leavelist, node, ret, current, current2, candidates, candidate;
+
+        worklist = [ top ];
+        leavelist = [];
+
+        while (worklist.length) {
+            node = worklist.pop();
+
+            if (node) {
+                if (visitor.enter) {
+                    ret = visitor.enter(node);
+                } else {
+                    ret = undefined;
+                }
+
+                if (ret === VisitorOption.Break) {
+                    return;
+                }
+
+                worklist.push(null);
+                leavelist.push(node);
+
+                if (ret !== VisitorOption.Skip) {
+                    candidates = VisitorKeys[node.type];
+                    current = candidates.length;
+                    while ((current -= 1) >= 0) {
+                        candidate = node[candidates[current]];
+                        if (candidate) {
+                            if (isArray(candidate)) {
+                                current2 = candidate.length;
+                                while ((current2 -= 1) >= 0) {
+                                    if (candidate[current2]) {
+                                        worklist.push(candidate[current2]);
+                                    }
+                                }
+                            } else {
+                                worklist.push(candidate);
+                            }
+                        }
+                    }
+                }
+            } 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
+
+    function upperBound(array, func) {
+        var diff, len, i, current;
+
+        len = array.length;
+        i = 0;
+
+        while (len) {
+            diff = len >>> 1;
+            current = i + diff;
+            if (func(array[current])) {
+                len = diff;
+            } else {
+                i = current + 1;
+                len -= diff + 1;
+            }
+        }
+        return i;
+    }
+
+    function lowerBound(array, func) {
+        var diff, len, i, current;
+
+        len = array.length;
+        i = 0;
+
+        while (len) {
+            diff = len >>> 1;
+            current = i + diff;
+            if (func(array[current])) {
+                i = current + 1;
+                len -= diff + 1;
+            } else {
+                len = diff;
+            }
+        }
+        return i;
+    }
+
+    function extendCommentRange(comment, tokens) {
+        var target, token;
+
+        target = upperBound(tokens, function search(token) {
+            return token.range[0] > comment.range[0];
+        });
+
+        comment.extendedRange = [comment.range[0], comment.range[1]];
+
+        if (target !== tokens.length) {
+            comment.extendedRange[1] = tokens[target].range[0];
+        }
+
+        target -= 1;
+        if (target >= 0) {
+            if (target < tokens.length) {
+                comment.extendedRange[0] = tokens[target].range[1];
+            } else if (token.length) {
+                comment.extendedRange[1] = tokens[tokens.length - 1].range[0];
+            }
+        }
+
+        return comment;
+    }
+
+    function attachComments(tree, providedComments, tokens) {
+        // At first, we should calculate extended comment ranges.
+        var comments = [], len, i;
+
+        if (!tree.range) {
+            throw new Error('attachComments needs range information');
+        }
+
+        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.
+        traverse(tree, {
+            cursor: 0,
+            enter: function (node) {
+                var comment;
+
+                while (this.cursor < comments.length) {
+                    comment = comments[this.cursor];
+                    if (comment.extendedRange[1] > node.range[0]) {
+                        break;
+                    }
+
+                    if (comment.extendedRange[1] === node.range[0]) {
+                        if (!node.leadingComments) {
+                            node.leadingComments = [];
+                        }
+                        node.leadingComments.push(comment);
+                        comments.splice(this.cursor, 1);
+                    } else {
+                        this.cursor += 1;
+                    }
+                }
+
+                // already out of owned node
+                if (this.cursor === comments.length) {
+                    return VisitorOption.Break;
+                }
+
+                if (comments[this.cursor].extendedRange[0] > node.range[1]) {
+                    return VisitorOption.Skip;
+                }
+            }
+        });
+
+        traverse(tree, {
+            cursor: 0,
+            leave: function (node) {
+                var comment;
+
+                while (this.cursor < comments.length) {
+                    comment = comments[this.cursor];
+                    if (node.range[1] < comment.extendedRange[0]) {
+                        break;
+                    }
+
+                    if (node.range[1] === comment.extendedRange[0]) {
+                        if (!node.trailingComments) {
+                            node.trailingComments = [];
+                        }
+                        node.trailingComments.push(comment);
+                        comments.splice(this.cursor, 1);
+                    } else {
+                        this.cursor += 1;
+                    }
+                }
+
+                // already out of owned node
+                if (this.cursor === comments.length) {
+                    return VisitorOption.Break;
+                }
+
+                if (comments[this.cursor].extendedRange[0] > node.range[1]) {
+                    return VisitorOption.Skip;
+                }
+            }
+        });
+
+        return tree;
+    }
+
     // Sync with package.json.
-    exports.version = '0.0.4-dev';
+    exports.version = '0.0.4';
 
     exports.generate = generate;
+    exports.traverse = traverse;
+    exports.attachComments = attachComments;
 
 }(typeof exports === 'undefined' ? (escodegen = {}) : exports));
 /* vim: set sw=4 ts=4 et tw=80 : */