From 1e5bfc2c55a60ddaad774fc7324c30f93cc99362 Mon Sep 17 00:00:00 2001 From: Artem Govorov Date: Fri, 6 Jun 2014 12:59:18 +1000 Subject: [PATCH] ecma 6 partial support: let and const --- acorn.js | 35 +- index.html | 35 +- test/tests.js | 1688 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1736 insertions(+), 22 deletions(-) diff --git a/acorn.js b/acorn.js index ad2f60905a..3326eab0af 100644 --- a/acorn.js +++ b/acorn.js @@ -51,9 +51,9 @@ var defaultOptions = exports.defaultOptions = { // `ecmaVersion` indicates the ECMAScript version to parse. Must - // be either 3 or 5. This - // influences support for strict mode, the set of reserved words, and - // support for getters and setter. + // be either 3, or 5, or 6. This + // influences support for strict mode, the set of reserved words, + // support for getters and setters and other features. ecmaVersion: 5, // Turn on `strictSemicolons` to prevent the parser from doing // automatic semicolon insertion. @@ -113,6 +113,8 @@ for (var opt in defaultOptions) if (!Object.prototype.hasOwnProperty.call(options, opt)) options[opt] = defaultOptions[opt]; sourceFile = options.sourceFile || null; + + isKeyword = options.ecmaVersion >= 6 ? isEcma6Keyword : isEcma5AndLessKeyword; } // The `getLineInfo` function is mostly useful when the @@ -276,6 +278,7 @@ var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"}; var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"}; var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"}; + var _let = {keyword: "let"}, _const = {keyword: "const"}; var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true}; var _this = {keyword: "this"}; @@ -296,7 +299,8 @@ "continue": _continue, "debugger": _debugger, "default": _default, "do": _do, "else": _else, "finally": _finally, "for": _for, "function": _function, "if": _if, "return": _return, "switch": _switch, - "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with, + "throw": _throw, "try": _try, "var": _var, "let": _let, "const": _const, + "while": _while, "with": _with, "null": _null, "true": _true, "false": _false, "new": _new, "in": _in, "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this, "typeof": {keyword: "typeof", prefix: true, beforeExpr: true}, @@ -415,7 +419,13 @@ // And the keywords. - var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"); + var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"; + + var isEcma5AndLessKeyword = makePredicate(ecma5AndLessKeywords); + + var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const"); + + var isKeyword = isEcma5AndLessKeyword; // ## Character categories @@ -1207,10 +1217,11 @@ labels.push(loopLabel); expect(_parenL); if (tokType === _semi) return parseFor(node, null); - if (tokType === _var) { + if (tokType === _var || tokType === _let) { + var varKind = tokType.keyword; var init = startNode(); next(); - parseVar(init, true); + parseVar(init, true, varKind); finishNode(init, "VariableDeclaration"); if (init.declarations.length === 1 && eat(_in)) return parseForIn(node, init); @@ -1308,9 +1319,11 @@ raise(node.start, "Missing catch or finally clause"); return finishNode(node, "TryStatement"); + case _const: + case _let: case _var: next(); - parseVar(node); + parseVar(node, false, starttype.keyword); semicolon(); return finishNode(node, "VariableDeclaration"); @@ -1421,15 +1434,15 @@ // Parse a list of variable declarations. - function parseVar(node, noIn) { + function parseVar(node, noIn, kind) { node.declarations = []; - node.kind = "var"; + node.kind = kind; for (;;) { var decl = startNode(); decl.id = parseIdent(); if (strict && isStrictBadIdWord(decl.id.name)) raise(decl.id.start, "Binding " + decl.id.name + " in strict mode"); - decl.init = eat(_eq) ? parseExpression(true, noIn) : null; + decl.init = eat(_eq) ? parseExpression(true, noIn) : (kind === _const.keyword ? unexpected() : null); node.declarations.push(finishNode(decl, "VariableDeclarator")); if (!eat(_comma)) break; } diff --git a/index.html b/index.html index 1f74c47c59..00c0182182 100644 --- a/index.html +++ b/index.html @@ -34,9 +34,9 @@ API, with the caveat that the SpiderMonkey-specific syntax return parseTopLevel(options.program); };

A second optional argument can be given to further configure the parser process. These options are recognized:

  var defaultOptions = exports.defaultOptions = {

ecmaVersion indicates the ECMAScript version to parse. Must -be either 3 or 5. This -influences support for strict mode, the set of reserved words, and -support for getters and setter.

    ecmaVersion: 5,

Turn on strictSemicolons to prevent the parser from doing +be either 3, or 5, or 6. This +influences support for strict mode, the set of reserved words, +support for getters and setters and other features.

    ecmaVersion: 5,

Turn on strictSemicolons to prevent the parser from doing automatic semicolon insertion.

    strictSemicolons: false,

When allowTrailingCommas is false, the parser will not allow trailing commas in array and object literals.

    allowTrailingCommas: true,

By default, reserved words are not enforced. Enable forbidReserved to enforce them. When this option has the @@ -73,6 +73,8 @@ file in every node's loc object.

for (var opt in defaultOptions) if (!Object.prototype.hasOwnProperty.call(options, opt)) options[opt] = defaultOptions[opt]; sourceFile = options.sourceFile || null; + + isKeyword = options.ecmaVersion >= 6 ? isEcma6Keyword : isEcma5AndLessKeyword; }

The getLineInfo function is mostly useful when the locations option is off (for performance reasons) and you want to find the line/column position for a given character @@ -172,6 +174,7 @@ continue jumps to that label.

var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"}; var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"}; var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"}; + var _let = {keyword: "let"}, _const = {keyword: "const"}; var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true}; var _this = {keyword: "this"};

The keywords that denote values.

  var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true};
   var _false = {keyword: "false", atomValue: false};

Some keywords are treated as regular operators. in sometimes @@ -180,7 +183,8 @@ we assign a variable name to it for quick comparing.

"continue": _continue, "debugger": _debugger, "default": _default, "do": _do, "else": _else, "finally": _finally, "for": _for, "function": _function, "if": _if, "return": _return, "switch": _switch, - "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with, + "throw": _throw, "try": _try, "var": _var, "let": _let, "const": _const, + "while": _while, "with": _with, "null": _null, "true": _true, "false": _false, "new": _new, "in": _in, "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this, "typeof": {keyword: "typeof", prefix: true, beforeExpr: true}, @@ -255,7 +259,13 @@ switch first dispatches on the lengths, to save on comparisons.

compareTo(words); } return new Function("str", f); - }

The ECMAScript 3 reserved word list.

  var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile");

ECMAScript 5 reserved words.

  var isReservedWord5 = makePredicate("class enum extends super const export import");

The additional reserved words in strict mode.

  var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield");

The forbidden variable names in strict mode.

  var isStrictBadIdWord = makePredicate("eval arguments");

And the keywords.

  var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this");

Character categories

Big ugly regular expressions that match characters in the + }

The ECMAScript 3 reserved word list.

  var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile");

ECMAScript 5 reserved words.

  var isReservedWord5 = makePredicate("class enum extends super const export import");

The additional reserved words in strict mode.

  var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield");

The forbidden variable names in strict mode.

  var isStrictBadIdWord = makePredicate("eval arguments");

And the keywords.

  var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";
+
+  var isEcma5AndLessKeyword = makePredicate(ecma5AndLessKeywords);
+
+  var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const");
+
+  var isKeyword = isEcma5AndLessKeyword;

Character categories

Big ugly regular expressions that match characters in the whitespace, identifier, and identifier-start categories. These are only applied when a character is found to actually have a code point above 128.

  var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
@@ -865,10 +875,11 @@ a regular for loop.

labels.push(loopLabel); expect(_parenL); if (tokType === _semi) return parseFor(node, null); - if (tokType === _var) { + if (tokType === _var || tokType === _let) { + var varKind = tokType.keyword; var init = startNode(); next(); - parseVar(init, true); + parseVar(init, true, varKind); finishNode(init, "VariableDeclaration"); if (init.declarations.length === 1 && eat(_in)) return parseForIn(node, init); @@ -958,9 +969,11 @@ adding statements to.

raise(node.start, "Missing catch or finally clause"); return finishNode(node, "TryStatement"); + case _const: + case _let: case _var: next(); - parseVar(node); + parseVar(node, false, starttype.keyword); semicolon(); return finishNode(node, "VariableDeclaration"); @@ -1047,15 +1060,15 @@ expression.

node.body = parseStatement(); labels.pop(); return finishNode(node, "ForInStatement"); - }

Parse a list of variable declarations.

  function parseVar(node, noIn) {
+  }

Parse a list of variable declarations.

  function parseVar(node, noIn, kind) {
     node.declarations = [];
-    node.kind = "var";
+    node.kind = kind;
     for (;;) {
       var decl = startNode();
       decl.id = parseIdent();
       if (strict && isStrictBadIdWord(decl.id.name))
         raise(decl.id.start, "Binding " + decl.id.name + " in strict mode");
-      decl.init = eat(_eq) ? parseExpression(true, noIn) : null;
+      decl.init = eat(_eq) ? parseExpression(true, noIn) : (kind === _const.keyword ? unexpected() : null);
       node.declarations.push(finishNode(decl, "VariableDeclarator"));
       if (!eat(_comma)) break;
     }
diff --git a/test/tests.js b/test/tests.js
index 4bc5b4d65d..0b47ada211 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -27262,6 +27262,1694 @@ testFail("var this = 10;", "Unexpected token (1:4)");
 
 testFail("throw\n10;", "Illegal newline after throw (1:5)");
 
+
+// ECMA < 6 mode should work as before
+
+testFail("const a;", "Unexpected token (1:6)");
+
+testFail("let x;", "Unexpected token (1:4)");
+
+testFail("const a = 1;", "Unexpected token (1:6)");
+
+testFail("let a = 1;", "Unexpected token (1:4)");
+
+testFail("for(const x = 0;;);", "Unexpected token (1:10)");
+
+testFail("for(let x = 0;;);", "Unexpected token (1:8)");
+
+test("let++", {
+  type: "Program",
+  start: 0,
+  end: 5,
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 5
+    }
+  },
+  body: [
+    {
+      type: "ExpressionStatement",
+      start: 0,
+      end: 5,
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 5
+        }
+      },
+      expression: {
+        type: "UpdateExpression",
+        start: 0,
+        end: 5,
+        loc: {
+          start: {
+            line: 1,
+            column: 0
+          },
+          end: {
+            line: 1,
+            column: 5
+          }
+        },
+        operator: "++",
+        prefix: false,
+        argument: {
+          type: "Identifier",
+          start: 0,
+          end: 3,
+          loc: {
+            start: {
+              line: 1,
+              column: 0
+            },
+            end: {
+              line: 1,
+              column: 3
+            }
+          },
+          name: "let"
+        }
+      }
+    }
+  ]
+});
+
+// ECMA 6 support
+
+test("let x", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "x",
+            loc: {
+              start: {
+                line: 1,
+                column: 4
+              },
+              end: {
+                line: 1,
+                column: 5
+              }
+            }
+          },
+          init: null,
+          loc: {
+            start: {
+              line: 1,
+              column: 4
+            },
+            end: {
+              line: 1,
+              column: 5
+            }
+          }
+        }
+      ],
+      kind: "let",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 5
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 5
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("let x, y;", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "x",
+            loc: {
+              start: {
+                line: 1,
+                column: 4
+              },
+              end: {
+                line: 1,
+                column: 5
+              }
+            }
+          },
+          init: null,
+          loc: {
+            start: {
+              line: 1,
+              column: 4
+            },
+            end: {
+              line: 1,
+              column: 5
+            }
+          }
+        },
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "y",
+            loc: {
+              start: {
+                line: 1,
+                column: 7
+              },
+              end: {
+                line: 1,
+                column: 8
+              }
+            }
+          },
+          init: null,
+          loc: {
+            start: {
+              line: 1,
+              column: 7
+            },
+            end: {
+              line: 1,
+              column: 8
+            }
+          }
+        }
+      ],
+      kind: "let",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 9
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 9
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("let x = 42", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "x",
+            loc: {
+              start: {
+                line: 1,
+                column: 4
+              },
+              end: {
+                line: 1,
+                column: 5
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 42,
+            loc: {
+              start: {
+                line: 1,
+                column: 8
+              },
+              end: {
+                line: 1,
+                column: 10
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 4
+            },
+            end: {
+              line: 1,
+              column: 10
+            }
+          }
+        }
+      ],
+      kind: "let",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 10
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 10
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("let eval = 42, arguments = 42", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "eval",
+            loc: {
+              start: {
+                line: 1,
+                column: 4
+              },
+              end: {
+                line: 1,
+                column: 8
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 42,
+            loc: {
+              start: {
+                line: 1,
+                column: 11
+              },
+              end: {
+                line: 1,
+                column: 13
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 4
+            },
+            end: {
+              line: 1,
+              column: 13
+            }
+          }
+        },
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "arguments",
+            loc: {
+              start: {
+                line: 1,
+                column: 15
+              },
+              end: {
+                line: 1,
+                column: 24
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 42,
+            loc: {
+              start: {
+                line: 1,
+                column: 27
+              },
+              end: {
+                line: 1,
+                column: 29
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 15
+            },
+            end: {
+              line: 1,
+              column: 29
+            }
+          }
+        }
+      ],
+      kind: "let",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 29
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 29
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("let x = 14, y = 3, z = 1977", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "x",
+            loc: {
+              start: {
+                line: 1,
+                column: 4
+              },
+              end: {
+                line: 1,
+                column: 5
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 14,
+            loc: {
+              start: {
+                line: 1,
+                column: 8
+              },
+              end: {
+                line: 1,
+                column: 10
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 4
+            },
+            end: {
+              line: 1,
+              column: 10
+            }
+          }
+        },
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "y",
+            loc: {
+              start: {
+                line: 1,
+                column: 12
+              },
+              end: {
+                line: 1,
+                column: 13
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 3,
+            loc: {
+              start: {
+                line: 1,
+                column: 16
+              },
+              end: {
+                line: 1,
+                column: 17
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 12
+            },
+            end: {
+              line: 1,
+              column: 17
+            }
+          }
+        },
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "z",
+            loc: {
+              start: {
+                line: 1,
+                column: 19
+              },
+              end: {
+                line: 1,
+                column: 20
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 1977,
+            loc: {
+              start: {
+                line: 1,
+                column: 23
+              },
+              end: {
+                line: 1,
+                column: 27
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 19
+            },
+            end: {
+              line: 1,
+              column: 27
+            }
+          }
+        }
+      ],
+      kind: "let",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 27
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 27
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("for(let x = 0;;);", {
+  type: "Program",
+  body: [
+    {
+      type: "ForStatement",
+      init: {
+        type: "VariableDeclaration",
+        declarations: [
+          {
+            type: "VariableDeclarator",
+            id: {
+              type: "Identifier",
+              name: "x",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 8
+                },
+                end: {
+                  line: 1,
+                  column: 9
+                }
+              }
+            },
+            init: {
+              type: "Literal",
+              value: 0,
+              loc: {
+                start: {
+                  line: 1,
+                  column: 12
+                },
+                end: {
+                  line: 1,
+                  column: 13
+                }
+              }
+            },
+            loc: {
+              start: {
+                line: 1,
+                column: 8
+              },
+              end: {
+                line: 1,
+                column: 13
+              }
+            }
+          }
+        ],
+        kind: "let",
+        loc: {
+          start: {
+            line: 1,
+            column: 4
+          },
+          end: {
+            line: 1,
+            column: 13
+          }
+        }
+      },
+      test: null,
+      update: null,
+      body: {
+        type: "EmptyStatement",
+        loc: {
+          start: {
+            line: 1,
+            column: 16
+          },
+          end: {
+            line: 1,
+            column: 17
+          }
+        }
+      },
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 17
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 17
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("for(let x = 0, y = 1;;);", {
+  type: "Program",
+  body: [
+    {
+      type: "ForStatement",
+      init: {
+        type: "VariableDeclaration",
+        declarations: [
+          {
+            type: "VariableDeclarator",
+            id: {
+              type: "Identifier",
+              name: "x",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 8
+                },
+                end: {
+                  line: 1,
+                  column: 9
+                }
+              }
+            },
+            init: {
+              type: "Literal",
+              value: 0,
+              loc: {
+                start: {
+                  line: 1,
+                  column: 12
+                },
+                end: {
+                  line: 1,
+                  column: 13
+                }
+              }
+            },
+            loc: {
+              start: {
+                line: 1,
+                column: 8
+              },
+              end: {
+                line: 1,
+                column: 13
+              }
+            }
+          },
+          {
+            type: "VariableDeclarator",
+            id: {
+              type: "Identifier",
+              name: "y",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 15
+                },
+                end: {
+                  line: 1,
+                  column: 16
+                }
+              }
+            },
+            init: {
+              type: "Literal",
+              value: 1,
+              loc: {
+                start: {
+                  line: 1,
+                  column: 19
+                },
+                end: {
+                  line: 1,
+                  column: 20
+                }
+              }
+            },
+            loc: {
+              start: {
+                line: 1,
+                column: 15
+              },
+              end: {
+                line: 1,
+                column: 20
+              }
+            }
+          }
+        ],
+        kind: "let",
+        loc: {
+          start: {
+            line: 1,
+            column: 4
+          },
+          end: {
+            line: 1,
+            column: 20
+          }
+        }
+      },
+      test: null,
+      update: null,
+      body: {
+        type: "EmptyStatement",
+        loc: {
+          start: {
+            line: 1,
+            column: 23
+          },
+          end: {
+            line: 1,
+            column: 24
+          }
+        }
+      },
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 24
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 24
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("for (let x in list) process(x);", {
+  type: "Program",
+  body: [
+    {
+      type: "ForInStatement",
+      left: {
+        type: "VariableDeclaration",
+        declarations: [
+          {
+            type: "VariableDeclarator",
+            id: {
+              type: "Identifier",
+              name: "x",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 9
+                },
+                end: {
+                  line: 1,
+                  column: 10
+                }
+              }
+            },
+            init: null,
+            loc: {
+              start: {
+                line: 1,
+                column: 9
+              },
+              end: {
+                line: 1,
+                column: 10
+              }
+            }
+          }
+        ],
+        kind: "let",
+        loc: {
+          start: {
+            line: 1,
+            column: 5
+          },
+          end: {
+            line: 1,
+            column: 10
+          }
+        }
+      },
+      right: {
+        type: "Identifier",
+        name: "list",
+        loc: {
+          start: {
+            line: 1,
+            column: 14
+          },
+          end: {
+            line: 1,
+            column: 18
+          }
+        }
+      },
+      body: {
+        type: "ExpressionStatement",
+        expression: {
+          type: "CallExpression",
+          callee: {
+            type: "Identifier",
+            name: "process",
+            loc: {
+              start: {
+                line: 1,
+                column: 20
+              },
+              end: {
+                line: 1,
+                column: 27
+              }
+            }
+          },
+          arguments: [
+            {
+              type: "Identifier",
+              name: "x",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 28
+                },
+                end: {
+                  line: 1,
+                  column: 29
+                }
+              }
+            }
+          ],
+          loc: {
+            start: {
+              line: 1,
+              column: 20
+            },
+            end: {
+              line: 1,
+              column: 30
+            }
+          }
+        },
+        loc: {
+          start: {
+            line: 1,
+            column: 20
+          },
+          end: {
+            line: 1,
+            column: 31
+          }
+        }
+      },
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 31
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 31
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("for (let x = 42 in list) process(x);", {
+  type: "Program",
+  body: [
+    {
+      type: "ForInStatement",
+      left: {
+        type: "VariableDeclaration",
+        declarations: [
+          {
+            type: "VariableDeclarator",
+            id: {
+              type: "Identifier",
+              name: "x",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 9
+                },
+                end: {
+                  line: 1,
+                  column: 10
+                }
+              }
+            },
+            init: {
+              type: "Literal",
+              value: 42,
+              loc: {
+                start: {
+                  line: 1,
+                  column: 13
+                },
+                end: {
+                  line: 1,
+                  column: 15
+                }
+              }
+            },
+            loc: {
+              start: {
+                line: 1,
+                column: 9
+              },
+              end: {
+                line: 1,
+                column: 15
+              }
+            }
+          }
+        ],
+        kind: "let",
+        loc: {
+          start: {
+            line: 1,
+            column: 5
+          },
+          end: {
+            line: 1,
+            column: 15
+          }
+        }
+      },
+      right: {
+        type: "Identifier",
+        name: "list",
+        loc: {
+          start: {
+            line: 1,
+            column: 19
+          },
+          end: {
+            line: 1,
+            column: 23
+          }
+        }
+      },
+      body: {
+        type: "ExpressionStatement",
+        expression: {
+          type: "CallExpression",
+          callee: {
+            type: "Identifier",
+            name: "process",
+            loc: {
+              start: {
+                line: 1,
+                column: 25
+              },
+              end: {
+                line: 1,
+                column: 32
+              }
+            }
+          },
+          arguments: [
+            {
+              type: "Identifier",
+              name: "x",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 33
+                },
+                end: {
+                  line: 1,
+                  column: 34
+                }
+              }
+            }
+          ],
+          loc: {
+            start: {
+              line: 1,
+              column: 25
+            },
+            end: {
+              line: 1,
+              column: 35
+            }
+          }
+        },
+        loc: {
+          start: {
+            line: 1,
+            column: 25
+          },
+          end: {
+            line: 1,
+            column: 36
+          }
+        }
+      },
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 36
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 36
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("for (let i = function() { return 10 in [] } in list) process(x);", {
+  type: "Program",
+  body: [
+    {
+      type: "ForInStatement",
+      left: {
+        type: "VariableDeclaration",
+        declarations: [
+          {
+            type: "VariableDeclarator",
+            id: {
+              type: "Identifier",
+              name: "i",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 9
+                },
+                end: {
+                  line: 1,
+                  column: 10
+                }
+              }
+            },
+            init: {
+              type: "FunctionExpression",
+              id: null,
+              params: [],
+              body: {
+                type: "BlockStatement",
+                body: [
+                  {
+                    type: "ReturnStatement",
+                    argument: {
+                      type: "BinaryExpression",
+                      left: {
+                        type: "Literal",
+                        value: 10,
+                        loc: {
+                          start: {
+                            line: 1,
+                            column: 33
+                          },
+                          end: {
+                            line: 1,
+                            column: 35
+                          }
+                        }
+                      },
+                      operator: "in",
+                      right: {
+                        type: "ArrayExpression",
+                        elements: [],
+                        loc: {
+                          start: {
+                            line: 1,
+                            column: 39
+                          },
+                          end: {
+                            line: 1,
+                            column: 41
+                          }
+                        }
+                      },
+                      loc: {
+                        start: {
+                          line: 1,
+                          column: 33
+                        },
+                        end: {
+                          line: 1,
+                          column: 41
+                        }
+                      }
+                    },
+                    loc: {
+                      start: {
+                        line: 1,
+                        column: 26
+                      },
+                      end: {
+                        line: 1,
+                        column: 41
+                      }
+                    }
+                  }
+                ],
+                loc: {
+                  start: {
+                    line: 1,
+                    column: 24
+                  },
+                  end: {
+                    line: 1,
+                    column: 43
+                  }
+                }
+              },
+              loc: {
+                start: {
+                  line: 1,
+                  column: 13
+                },
+                end: {
+                  line: 1,
+                  column: 43
+                }
+              }
+            },
+            loc: {
+              start: {
+                line: 1,
+                column: 9
+              },
+              end: {
+                line: 1,
+                column: 43
+              }
+            }
+          }
+        ],
+        kind: "let",
+        loc: {
+          start: {
+            line: 1,
+            column: 5
+          },
+          end: {
+            line: 1,
+            column: 43
+          }
+        }
+      },
+      right: {
+        type: "Identifier",
+        name: "list",
+        loc: {
+          start: {
+            line: 1,
+            column: 47
+          },
+          end: {
+            line: 1,
+            column: 51
+          }
+        }
+      },
+      body: {
+        type: "ExpressionStatement",
+        expression: {
+          type: "CallExpression",
+          callee: {
+            type: "Identifier",
+            name: "process",
+            loc: {
+              start: {
+                line: 1,
+                column: 53
+              },
+              end: {
+                line: 1,
+                column: 60
+              }
+            }
+          },
+          arguments: [
+            {
+              type: "Identifier",
+              name: "x",
+              loc: {
+                start: {
+                  line: 1,
+                  column: 61
+                },
+                end: {
+                  line: 1,
+                  column: 62
+                }
+              }
+            }
+          ],
+          loc: {
+            start: {
+              line: 1,
+              column: 53
+            },
+            end: {
+              line: 1,
+              column: 63
+            }
+          }
+        },
+        loc: {
+          start: {
+            line: 1,
+            column: 53
+          },
+          end: {
+            line: 1,
+            column: 64
+          }
+        }
+      },
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 64
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 64
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("const x = 42", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "x",
+            loc: {
+              start: {
+                line: 1,
+                column: 6
+              },
+              end: {
+                line: 1,
+                column: 7
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 42,
+            loc: {
+              start: {
+                line: 1,
+                column: 10
+              },
+              end: {
+                line: 1,
+                column: 12
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 6
+            },
+            end: {
+              line: 1,
+              column: 12
+            }
+          }
+        }
+      ],
+      kind: "const",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 12
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 12
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("const eval = 42, arguments = 42", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "eval",
+            loc: {
+              start: {
+                line: 1,
+                column: 6
+              },
+              end: {
+                line: 1,
+                column: 10
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 42,
+            loc: {
+              start: {
+                line: 1,
+                column: 13
+              },
+              end: {
+                line: 1,
+                column: 15
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 6
+            },
+            end: {
+              line: 1,
+              column: 15
+            }
+          }
+        },
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "arguments",
+            loc: {
+              start: {
+                line: 1,
+                column: 17
+              },
+              end: {
+                line: 1,
+                column: 26
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 42,
+            loc: {
+              start: {
+                line: 1,
+                column: 29
+              },
+              end: {
+                line: 1,
+                column: 31
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 17
+            },
+            end: {
+              line: 1,
+              column: 31
+            }
+          }
+        }
+      ],
+      kind: "const",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 31
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 31
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+test("const x = 14, y = 3, z = 1977", {
+  type: "Program",
+  body: [
+    {
+      type: "VariableDeclaration",
+      declarations: [
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "x",
+            loc: {
+              start: {
+                line: 1,
+                column: 6
+              },
+              end: {
+                line: 1,
+                column: 7
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 14,
+            loc: {
+              start: {
+                line: 1,
+                column: 10
+              },
+              end: {
+                line: 1,
+                column: 12
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 6
+            },
+            end: {
+              line: 1,
+              column: 12
+            }
+          }
+        },
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "y",
+            loc: {
+              start: {
+                line: 1,
+                column: 14
+              },
+              end: {
+                line: 1,
+                column: 15
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 3,
+            loc: {
+              start: {
+                line: 1,
+                column: 18
+              },
+              end: {
+                line: 1,
+                column: 19
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 14
+            },
+            end: {
+              line: 1,
+              column: 19
+            }
+          }
+        },
+        {
+          type: "VariableDeclarator",
+          id: {
+            type: "Identifier",
+            name: "z",
+            loc: {
+              start: {
+                line: 1,
+                column: 21
+              },
+              end: {
+                line: 1,
+                column: 22
+              }
+            }
+          },
+          init: {
+            type: "Literal",
+            value: 1977,
+            loc: {
+              start: {
+                line: 1,
+                column: 25
+              },
+              end: {
+                line: 1,
+                column: 29
+              }
+            }
+          },
+          loc: {
+            start: {
+              line: 1,
+              column: 21
+            },
+            end: {
+              line: 1,
+              column: 29
+            }
+          }
+        }
+      ],
+      kind: "const",
+      loc: {
+        start: {
+          line: 1,
+          column: 0
+        },
+        end: {
+          line: 1,
+          column: 29
+        }
+      }
+    }
+  ],
+  loc: {
+    start: {
+      line: 1,
+      column: 0
+    },
+    end: {
+      line: 1,
+      column: 29
+    }
+  }
+}, {ecmaVersion: 6, locations: true});
+
+testFail("const a;", "Unexpected token (1:7)", {ecmaVersion: 6});
+
+testFail("for(const x = 0;;);", "Unexpected token (1:4)", {ecmaVersion: 6});
+
 // Assertion Tests
 (function() {
   var actualComments = [],