[Pkg-javascript-commits] [node-esutils] 01/09: Import Upstream version 2.0.2

Praveen Arimbrathodiyil praveen at moszumanska.debian.org
Mon Oct 10 10:09:27 UTC 2016


This is an automated email from the git hooks/post-receive script.

praveen pushed a commit to branch master
in repository node-esutils.

commit d3b1ed0dcf8a73b65352c4d28309cc09c58bfb57
Author: Praveen Arimbrathodiyil <praveen at debian.org>
Date:   Mon Oct 10 15:04:16 2016 +0530

    Import Upstream version 2.0.2
---
 .gitignore                         |   1 +
 .jshintrc                          |  16 ++
 .travis.yml                        |   8 +
 LICENSE.BSD                        |  19 ++
 README.md                          | 169 ++++++++++++++++
 lib/ast.js                         | 144 +++++++++++++
 lib/code.js                        | 135 +++++++++++++
 lib/keyword.js                     | 165 +++++++++++++++
 lib/utils.js                       |  33 +++
 package.json                       |  49 +++++
 test/ast.coffee                    | 175 ++++++++++++++++
 test/code.coffee                   | 183 +++++++++++++++++
 test/keyword.coffee                | 400 +++++++++++++++++++++++++++++++++++++
 tools/generate-identifier-regex.js |  87 ++++++++
 14 files changed, 1584 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3c3629e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+node_modules
diff --git a/.jshintrc b/.jshintrc
new file mode 100644
index 0000000..f642dae
--- /dev/null
+++ b/.jshintrc
@@ -0,0 +1,16 @@
+{
+  "curly": true,
+  "eqeqeq": true,
+  "immed": true,
+  "eqnull": true,
+  "latedef": true,
+  "noarg": true,
+  "noempty": true,
+  "quotmark": "single",
+  "undef": true,
+  "unused": true,
+  "strict": true,
+  "trailing": true,
+
+  "node": true
+}
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4b59f8d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,8 @@
+language: node_js
+node_js:
+    - "0.10"
+    - "0.11"
+
+matrix:
+    allow_failures:
+        - node_js: "0.11"
diff --git a/LICENSE.BSD b/LICENSE.BSD
new file mode 100644
index 0000000..3e580c3
--- /dev/null
+++ b/LICENSE.BSD
@@ -0,0 +1,19 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..610d8bd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,169 @@
+### esutils [![Build Status](https://secure.travis-ci.org/estools/esutils.svg)](http://travis-ci.org/estools/esutils)
+esutils ([esutils](http://github.com/estools/esutils)) is
+utility box for ECMAScript language tools.
+
+### API
+
+### ast
+
+#### ast.isExpression(node)
+
+Returns true if `node` is an Expression as defined in ECMA262 edition 5.1 section
+[11](https://es5.github.io/#x11).
+
+#### ast.isStatement(node)
+
+Returns true if `node` is a Statement as defined in ECMA262 edition 5.1 section
+[12](https://es5.github.io/#x12).
+
+#### ast.isIterationStatement(node)
+
+Returns true if `node` is an IterationStatement as defined in ECMA262 edition
+5.1 section [12.6](https://es5.github.io/#x12.6).
+
+#### ast.isSourceElement(node)
+
+Returns true if `node` is a SourceElement as defined in ECMA262 edition 5.1
+section [14](https://es5.github.io/#x14).
+
+#### ast.trailingStatement(node)
+
+Returns `Statement?` if `node` has trailing `Statement`.
+```js
+if (cond)
+    consequent;
+```
+When taking this `IfStatement`, returns `consequent;` statement.
+
+#### ast.isProblematicIfStatement(node)
+
+Returns true if `node` is a problematic IfStatement. If `node` is a problematic `IfStatement`, `node` cannot be represented as an one on one JavaScript code.
+```js
+{
+    type: 'IfStatement',
+    consequent: {
+        type: 'WithStatement',
+        body: {
+            type: 'IfStatement',
+            consequent: {type: 'EmptyStatement'}
+        }
+    },
+    alternate: {type: 'EmptyStatement'}
+}
+```
+The above node cannot be represented as a JavaScript code, since the top level `else` alternate belongs to an inner `IfStatement`.
+
+
+### code
+
+#### code.isDecimalDigit(code)
+
+Return true if provided code is decimal digit.
+
+#### code.isHexDigit(code)
+
+Return true if provided code is hexadecimal digit.
+
+#### code.isOctalDigit(code)
+
+Return true if provided code is octal digit.
+
+#### code.isWhiteSpace(code)
+
+Return true if provided code is white space. White space characters are formally defined in ECMA262.
+
+#### code.isLineTerminator(code)
+
+Return true if provided code is line terminator. Line terminator characters are formally defined in ECMA262.
+
+#### code.isIdentifierStart(code)
+
+Return true if provided code can be the first character of ECMA262 Identifier. They are formally defined in ECMA262.
+
+#### code.isIdentifierPart(code)
+
+Return true if provided code can be the trailing character of ECMA262 Identifier. They are formally defined in ECMA262.
+
+### keyword
+
+#### keyword.isKeywordES5(id, strict)
+
+Returns `true` if provided identifier string is a Keyword or Future Reserved Word
+in ECMA262 edition 5.1. They are formally defined in ECMA262 sections
+[7.6.1.1](http://es5.github.io/#x7.6.1.1) and [7.6.1.2](http://es5.github.io/#x7.6.1.2),
+respectively. If the `strict` flag is truthy, this function additionally checks whether
+`id` is a Keyword or Future Reserved Word under strict mode.
+
+#### keyword.isKeywordES6(id, strict)
+
+Returns `true` if provided identifier string is a Keyword or Future Reserved Word
+in ECMA262 edition 6. They are formally defined in ECMA262 sections
+[11.6.2.1](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-keywords) and
+[11.6.2.2](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-future-reserved-words),
+respectively. If the `strict` flag is truthy, this function additionally checks whether
+`id` is a Keyword or Future Reserved Word under strict mode.
+
+#### keyword.isReservedWordES5(id, strict)
+
+Returns `true` if provided identifier string is a Reserved Word in ECMA262 edition 5.1.
+They are formally defined in ECMA262 section [7.6.1](http://es5.github.io/#x7.6.1).
+If the `strict` flag is truthy, this function additionally checks whether `id`
+is a Reserved Word under strict mode.
+
+#### keyword.isReservedWordES6(id, strict)
+
+Returns `true` if provided identifier string is a Reserved Word in ECMA262 edition 6.
+They are formally defined in ECMA262 section [11.6.2](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-reserved-words).
+If the `strict` flag is truthy, this function additionally checks whether `id`
+is a Reserved Word under strict mode.
+
+#### keyword.isRestrictedWord(id)
+
+Returns `true` if provided identifier string is one of `eval` or `arguments`.
+They are restricted in strict mode code throughout ECMA262 edition 5.1 and
+in ECMA262 edition 6 section [12.1.1](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-identifiers-static-semantics-early-errors).
+
+#### keyword.isIdentifierName(id)
+
+Return true if provided identifier string is an IdentifierName as specified in
+ECMA262 edition 5.1 section [7.6](https://es5.github.io/#x7.6).
+
+#### keyword.isIdentifierES5(id, strict)
+
+Return true if provided identifier string is an Identifier as specified in
+ECMA262 edition 5.1 section [7.6](https://es5.github.io/#x7.6). If the `strict`
+flag is truthy, this function additionally checks whether `id` is an Identifier
+under strict mode.
+
+#### keyword.isIdentifierES6(id, strict)
+
+Return true if provided identifier string is an Identifier as specified in
+ECMA262 edition 6 section [12.1](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-identifiers).
+If the `strict` flag is truthy, this function additionally checks whether `id`
+is an Identifier under strict mode.
+
+### License
+
+Copyright (C) 2013 [Yusuke Suzuki](http://github.com/Constellation)
+ (twitter: [@Constellation](http://twitter.com/Constellation)) and other contributors.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/ast.js b/lib/ast.js
new file mode 100644
index 0000000..8faadae
--- /dev/null
+++ b/lib/ast.js
@@ -0,0 +1,144 @@
+/*
+  Copyright (C) 2013 Yusuke Suzuki <utatane.tea at gmail.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+(function () {
+    'use strict';
+
+    function isExpression(node) {
+        if (node == null) { return false; }
+        switch (node.type) {
+            case 'ArrayExpression':
+            case 'AssignmentExpression':
+            case 'BinaryExpression':
+            case 'CallExpression':
+            case 'ConditionalExpression':
+            case 'FunctionExpression':
+            case 'Identifier':
+            case 'Literal':
+            case 'LogicalExpression':
+            case 'MemberExpression':
+            case 'NewExpression':
+            case 'ObjectExpression':
+            case 'SequenceExpression':
+            case 'ThisExpression':
+            case 'UnaryExpression':
+            case 'UpdateExpression':
+                return true;
+        }
+        return false;
+    }
+
+    function isIterationStatement(node) {
+        if (node == null) { return false; }
+        switch (node.type) {
+            case 'DoWhileStatement':
+            case 'ForInStatement':
+            case 'ForStatement':
+            case 'WhileStatement':
+                return true;
+        }
+        return false;
+    }
+
+    function isStatement(node) {
+        if (node == null) { return false; }
+        switch (node.type) {
+            case 'BlockStatement':
+            case 'BreakStatement':
+            case 'ContinueStatement':
+            case 'DebuggerStatement':
+            case 'DoWhileStatement':
+            case 'EmptyStatement':
+            case 'ExpressionStatement':
+            case 'ForInStatement':
+            case 'ForStatement':
+            case 'IfStatement':
+            case 'LabeledStatement':
+            case 'ReturnStatement':
+            case 'SwitchStatement':
+            case 'ThrowStatement':
+            case 'TryStatement':
+            case 'VariableDeclaration':
+            case 'WhileStatement':
+            case 'WithStatement':
+                return true;
+        }
+        return false;
+    }
+
+    function isSourceElement(node) {
+      return isStatement(node) || node != null && node.type === 'FunctionDeclaration';
+    }
+
+    function trailingStatement(node) {
+        switch (node.type) {
+        case 'IfStatement':
+            if (node.alternate != null) {
+                return node.alternate;
+            }
+            return node.consequent;
+
+        case 'LabeledStatement':
+        case 'ForStatement':
+        case 'ForInStatement':
+        case 'WhileStatement':
+        case 'WithStatement':
+            return node.body;
+        }
+        return null;
+    }
+
+    function isProblematicIfStatement(node) {
+        var current;
+
+        if (node.type !== 'IfStatement') {
+            return false;
+        }
+        if (node.alternate == null) {
+            return false;
+        }
+        current = node.consequent;
+        do {
+            if (current.type === 'IfStatement') {
+                if (current.alternate == null)  {
+                    return true;
+                }
+            }
+            current = trailingStatement(current);
+        } while (current);
+
+        return false;
+    }
+
+    module.exports = {
+        isExpression: isExpression,
+        isStatement: isStatement,
+        isIterationStatement: isIterationStatement,
+        isSourceElement: isSourceElement,
+        isProblematicIfStatement: isProblematicIfStatement,
+
+        trailingStatement: trailingStatement
+    };
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/lib/code.js b/lib/code.js
new file mode 100644
index 0000000..2a7c19d
--- /dev/null
+++ b/lib/code.js
@@ -0,0 +1,135 @@
+/*
+  Copyright (C) 2013-2014 Yusuke Suzuki <utatane.tea at gmail.com>
+  Copyright (C) 2014 Ivan Nikulin <ifaaan at gmail.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+(function () {
+    'use strict';
+
+    var ES6Regex, ES5Regex, NON_ASCII_WHITESPACES, IDENTIFIER_START, IDENTIFIER_PART, ch;
+
+    // See `tools/generate-identifier-regex.js`.
+    ES5Regex = {
+        // ECMAScript 5.1/Unicode v7.0.0 NonAsciiIdentifierStart:
+        NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u085 [...]
+        // ECMAScript 5.1/Unicode v7.0.0 NonAsciiIdentifierPart:
+        NonAsciiIdentifierPart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\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\u0800-\u [...]
+    };
+
+    ES6Regex = {
+        // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierStart:
+        NonAsciiIdentifierStart: /[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u085 [...]
+        // ECMAScript 6/Unicode v7.0.0 NonAsciiIdentifierPart:
+        NonAsciiIdentifierPart: /[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\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\u0800-\u08 [...]
+    };
+
+    function isDecimalDigit(ch) {
+        return 0x30 <= ch && ch <= 0x39;  // 0..9
+    }
+
+    function isHexDigit(ch) {
+        return 0x30 <= ch && ch <= 0x39 ||  // 0..9
+            0x61 <= ch && ch <= 0x66 ||     // a..f
+            0x41 <= ch && ch <= 0x46;       // A..F
+    }
+
+    function isOctalDigit(ch) {
+        return ch >= 0x30 && ch <= 0x37;  // 0..7
+    }
+
+    // 7.2 White Space
+
+    NON_ASCII_WHITESPACES = [
+        0x1680, 0x180E,
+        0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A,
+        0x202F, 0x205F,
+        0x3000,
+        0xFEFF
+    ];
+
+    function isWhiteSpace(ch) {
+        return ch === 0x20 || ch === 0x09 || ch === 0x0B || ch === 0x0C || ch === 0xA0 ||
+            ch >= 0x1680 && NON_ASCII_WHITESPACES.indexOf(ch) >= 0;
+    }
+
+    // 7.3 Line Terminators
+
+    function isLineTerminator(ch) {
+        return ch === 0x0A || ch === 0x0D || ch === 0x2028 || ch === 0x2029;
+    }
+
+    // 7.6 Identifier Names and Identifiers
+
+    function fromCodePoint(cp) {
+        if (cp <= 0xFFFF) { return String.fromCharCode(cp); }
+        var cu1 = String.fromCharCode(Math.floor((cp - 0x10000) / 0x400) + 0xD800);
+        var cu2 = String.fromCharCode(((cp - 0x10000) % 0x400) + 0xDC00);
+        return cu1 + cu2;
+    }
+
+    IDENTIFIER_START = new Array(0x80);
+    for(ch = 0; ch < 0x80; ++ch) {
+        IDENTIFIER_START[ch] =
+            ch >= 0x61 && ch <= 0x7A ||  // a..z
+            ch >= 0x41 && ch <= 0x5A ||  // A..Z
+            ch === 0x24 || ch === 0x5F;  // $ (dollar) and _ (underscore)
+    }
+
+    IDENTIFIER_PART = new Array(0x80);
+    for(ch = 0; ch < 0x80; ++ch) {
+        IDENTIFIER_PART[ch] =
+            ch >= 0x61 && ch <= 0x7A ||  // a..z
+            ch >= 0x41 && ch <= 0x5A ||  // A..Z
+            ch >= 0x30 && ch <= 0x39 ||  // 0..9
+            ch === 0x24 || ch === 0x5F;  // $ (dollar) and _ (underscore)
+    }
+
+    function isIdentifierStartES5(ch) {
+        return ch < 0x80 ? IDENTIFIER_START[ch] : ES5Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch));
+    }
+
+    function isIdentifierPartES5(ch) {
+        return ch < 0x80 ? IDENTIFIER_PART[ch] : ES5Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch));
+    }
+
+    function isIdentifierStartES6(ch) {
+        return ch < 0x80 ? IDENTIFIER_START[ch] : ES6Regex.NonAsciiIdentifierStart.test(fromCodePoint(ch));
+    }
+
+    function isIdentifierPartES6(ch) {
+        return ch < 0x80 ? IDENTIFIER_PART[ch] : ES6Regex.NonAsciiIdentifierPart.test(fromCodePoint(ch));
+    }
+
+    module.exports = {
+        isDecimalDigit: isDecimalDigit,
+        isHexDigit: isHexDigit,
+        isOctalDigit: isOctalDigit,
+        isWhiteSpace: isWhiteSpace,
+        isLineTerminator: isLineTerminator,
+        isIdentifierStartES5: isIdentifierStartES5,
+        isIdentifierPartES5: isIdentifierPartES5,
+        isIdentifierStartES6: isIdentifierStartES6,
+        isIdentifierPartES6: isIdentifierPartES6
+    };
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/lib/keyword.js b/lib/keyword.js
new file mode 100644
index 0000000..13c8c6a
--- /dev/null
+++ b/lib/keyword.js
@@ -0,0 +1,165 @@
+/*
+  Copyright (C) 2013 Yusuke Suzuki <utatane.tea at gmail.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+(function () {
+    'use strict';
+
+    var code = require('./code');
+
+    function isStrictModeReservedWordES6(id) {
+        switch (id) {
+        case 'implements':
+        case 'interface':
+        case 'package':
+        case 'private':
+        case 'protected':
+        case 'public':
+        case 'static':
+        case 'let':
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    function isKeywordES5(id, strict) {
+        // yield should not be treated as keyword under non-strict mode.
+        if (!strict && id === 'yield') {
+            return false;
+        }
+        return isKeywordES6(id, strict);
+    }
+
+    function isKeywordES6(id, strict) {
+        if (strict && isStrictModeReservedWordES6(id)) {
+            return true;
+        }
+
+        switch (id.length) {
+        case 2:
+            return (id === 'if') || (id === 'in') || (id === 'do');
+        case 3:
+            return (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try');
+        case 4:
+            return (id === 'this') || (id === 'else') || (id === 'case') ||
+                (id === 'void') || (id === 'with') || (id === 'enum');
+        case 5:
+            return (id === 'while') || (id === 'break') || (id === 'catch') ||
+                (id === 'throw') || (id === 'const') || (id === 'yield') ||
+                (id === 'class') || (id === 'super');
+        case 6:
+            return (id === 'return') || (id === 'typeof') || (id === 'delete') ||
+                (id === 'switch') || (id === 'export') || (id === 'import');
+        case 7:
+            return (id === 'default') || (id === 'finally') || (id === 'extends');
+        case 8:
+            return (id === 'function') || (id === 'continue') || (id === 'debugger');
+        case 10:
+            return (id === 'instanceof');
+        default:
+            return false;
+        }
+    }
+
+    function isReservedWordES5(id, strict) {
+        return id === 'null' || id === 'true' || id === 'false' || isKeywordES5(id, strict);
+    }
+
+    function isReservedWordES6(id, strict) {
+        return id === 'null' || id === 'true' || id === 'false' || isKeywordES6(id, strict);
+    }
+
+    function isRestrictedWord(id) {
+        return id === 'eval' || id === 'arguments';
+    }
+
+    function isIdentifierNameES5(id) {
+        var i, iz, ch;
+
+        if (id.length === 0) { return false; }
+
+        ch = id.charCodeAt(0);
+        if (!code.isIdentifierStartES5(ch)) {
+            return false;
+        }
+
+        for (i = 1, iz = id.length; i < iz; ++i) {
+            ch = id.charCodeAt(i);
+            if (!code.isIdentifierPartES5(ch)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    function decodeUtf16(lead, trail) {
+        return (lead - 0xD800) * 0x400 + (trail - 0xDC00) + 0x10000;
+    }
+
+    function isIdentifierNameES6(id) {
+        var i, iz, ch, lowCh, check;
+
+        if (id.length === 0) { return false; }
+
+        check = code.isIdentifierStartES6;
+        for (i = 0, iz = id.length; i < iz; ++i) {
+            ch = id.charCodeAt(i);
+            if (0xD800 <= ch && ch <= 0xDBFF) {
+                ++i;
+                if (i >= iz) { return false; }
+                lowCh = id.charCodeAt(i);
+                if (!(0xDC00 <= lowCh && lowCh <= 0xDFFF)) {
+                    return false;
+                }
+                ch = decodeUtf16(ch, lowCh);
+            }
+            if (!check(ch)) {
+                return false;
+            }
+            check = code.isIdentifierPartES6;
+        }
+        return true;
+    }
+
+    function isIdentifierES5(id, strict) {
+        return isIdentifierNameES5(id) && !isReservedWordES5(id, strict);
+    }
+
+    function isIdentifierES6(id, strict) {
+        return isIdentifierNameES6(id) && !isReservedWordES6(id, strict);
+    }
+
+    module.exports = {
+        isKeywordES5: isKeywordES5,
+        isKeywordES6: isKeywordES6,
+        isReservedWordES5: isReservedWordES5,
+        isReservedWordES6: isReservedWordES6,
+        isRestrictedWord: isRestrictedWord,
+        isIdentifierNameES5: isIdentifierNameES5,
+        isIdentifierNameES6: isIdentifierNameES6,
+        isIdentifierES5: isIdentifierES5,
+        isIdentifierES6: isIdentifierES6
+    };
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/lib/utils.js b/lib/utils.js
new file mode 100644
index 0000000..ce18faa
--- /dev/null
+++ b/lib/utils.js
@@ -0,0 +1,33 @@
+/*
+  Copyright (C) 2013 Yusuke Suzuki <utatane.tea at gmail.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+(function () {
+    'use strict';
+
+    exports.ast = require('./ast');
+    exports.code = require('./code');
+    exports.keyword = require('./keyword');
+}());
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ddce20b
--- /dev/null
+++ b/package.json
@@ -0,0 +1,49 @@
+{
+  "name": "esutils",
+  "description": "utility box for ECMAScript language tools",
+  "homepage": "https://github.com/estools/esutils",
+  "main": "lib/utils.js",
+  "version": "2.0.2",
+  "engines": {
+    "node": ">=0.10.0"
+  },
+  "directories": {
+    "lib": "./lib"
+  },
+  "files": [
+    "LICENSE.BSD",
+    "README.md",
+    "lib"
+  ],
+  "maintainers": [
+    {
+      "name": "Yusuke Suzuki",
+      "email": "utatane.tea at gmail.com",
+      "web": "http://github.com/Constellation"
+    }
+  ],
+  "repository": {
+    "type": "git",
+    "url": "http://github.com/estools/esutils.git"
+  },
+  "devDependencies": {
+    "chai": "~1.7.2",
+    "coffee-script": "~1.6.3",
+    "jshint": "2.6.3",
+    "mocha": "~2.2.1",
+    "regenerate": "~1.2.1",
+    "unicode-7.0.0": "^0.1.5"
+  },
+  "licenses": [
+    {
+      "type": "BSD",
+      "url": "http://github.com/estools/esutils/raw/master/LICENSE.BSD"
+    }
+  ],
+  "scripts": {
+    "test": "npm run-script lint && npm run-script unit-test",
+    "lint": "jshint lib/*.js",
+    "unit-test": "mocha --compilers coffee:coffee-script -R spec",
+    "generate-regex": "node tools/generate-identifier-regex.js"
+  }
+}
diff --git a/test/ast.coffee b/test/ast.coffee
new file mode 100644
index 0000000..021a7cd
--- /dev/null
+++ b/test/ast.coffee
@@ -0,0 +1,175 @@
+# Copyright (C) 2013 Yusuke Suzuki <utatane.tea at gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+'use strict'
+
+expect = require('chai').expect
+esutils = require '../'
+
+EMPTY = {type: 'EmptyStatement'}
+
+describe 'ast', ->
+    describe 'isExpression', ->
+        it 'returns false if input is not node', ->
+            expect(esutils.ast.isExpression(0)).to.be.false
+            expect(esutils.ast.isExpression(null)).to.be.false
+            expect(esutils.ast.isExpression(undefined)).to.be.false
+            expect(esutils.ast.isExpression({})).to.be.false
+            expect(esutils.ast.isExpression({type: null})).to.be.false
+            expect(esutils.ast.isExpression({type: undefined})).to.be.false
+
+        it 'returns true if provided node is expression', ->
+            expect(esutils.ast.isExpression({type: "ThisExpression"})).to.be.true
+            expect(esutils.ast.isExpression({type: "Literal", value: 0})).to.be.true
+
+        it 'returns false if provided node is not expression', ->
+            expect(esutils.ast.isExpression({type: "ExpressionStatement"})).to.be.false
+            expect(esutils.ast.isExpression({type: "Program"})).to.be.false
+
+
+    describe 'isIterationStatement', ->
+        it 'returns false if input is not node', ->
+            expect(esutils.ast.isIterationStatement(0)).to.be.false
+            expect(esutils.ast.isIterationStatement(null)).to.be.false
+            expect(esutils.ast.isIterationStatement(undefined)).to.be.false
+            expect(esutils.ast.isIterationStatement({})).to.be.false
+            expect(esutils.ast.isIterationStatement({type: null})).to.be.false
+            expect(esutils.ast.isIterationStatement({type: undefined})).to.be.false
+
+        it 'returns true if provided node is iteration statement', ->
+            expect(esutils.ast.isIterationStatement({type: "ForInStatement"})).to.be.true
+            expect(esutils.ast.isIterationStatement({type: "DoWhileStatement"})).to.be.true
+
+        it 'returns false if provided node is not iteration statement', ->
+            expect(esutils.ast.isIterationStatement({type: "ExpressionStatement"})).to.be.false
+            expect(esutils.ast.isIterationStatement({type: "ThisExpression"})).to.be.false
+
+
+    describe 'isStatement', ->
+        it 'returns false if input is not node', ->
+            expect(esutils.ast.isStatement(0)).to.be.false
+            expect(esutils.ast.isStatement(null)).to.be.false
+            expect(esutils.ast.isStatement(undefined)).to.be.false
+            expect(esutils.ast.isStatement({})).to.be.false
+            expect(esutils.ast.isStatement({type: null})).to.be.false
+            expect(esutils.ast.isStatement({type: undefined})).to.be.false
+
+        it 'returns true if provided node is statement', ->
+            expect(esutils.ast.isStatement({type: "ExpressionStatement"})).to.be.true
+            expect(esutils.ast.isStatement({type: "WhileStatement"})).to.be.true
+
+        it 'returns false if provided node is not statement', ->
+            expect(esutils.ast.isStatement({type: "ThisExpression"})).to.be.false
+            expect(esutils.ast.isStatement({type: "FunctionDeclaration"})).to.be.false
+            expect(esutils.ast.isStatement({type: "Program"})).to.be.false
+
+
+    describe 'isSourceElement', ->
+        it 'returns false if input is not node', ->
+            expect(esutils.ast.isSourceElement(0)).to.be.false
+            expect(esutils.ast.isSourceElement(null)).to.be.false
+            expect(esutils.ast.isSourceElement(undefined)).to.be.false
+            expect(esutils.ast.isSourceElement({})).to.be.false
+            expect(esutils.ast.isSourceElement({type: null})).to.be.false
+            expect(esutils.ast.isSourceElement({type: undefined})).to.be.false
+
+        it 'returns true if provided node is source element', ->
+            expect(esutils.ast.isSourceElement({type: "ExpressionStatement"})).to.be.true
+            expect(esutils.ast.isSourceElement({type: "WhileStatement"})).to.be.true
+            expect(esutils.ast.isSourceElement({type: "FunctionDeclaration"})).to.be.true
+
+        it 'returns false if provided node is not source element', ->
+            expect(esutils.ast.isSourceElement({type: "ThisExpression"})).to.be.false
+            expect(esutils.ast.isSourceElement({type: "Program"})).to.be.false
+
+    describe 'trailingStatement', ->
+        it 'returns trailing statement if node has it', ->
+            expect(esutils.ast.trailingStatement({type: 'WhileStatement', body: EMPTY})).to.be.eq EMPTY
+            expect(esutils.ast.trailingStatement({type: 'WithStatement', body: EMPTY})).to.be.eq EMPTY
+            expect(esutils.ast.trailingStatement({type: 'ForStatement', body: EMPTY})).to.be.eq EMPTY
+            expect(esutils.ast.trailingStatement({type: 'ForInStatement', body: EMPTY})).to.be.eq EMPTY
+            expect(esutils.ast.trailingStatement({type: 'IfStatement', consequent: EMPTY})).to.be.eq EMPTY
+            expect(esutils.ast.trailingStatement({type: 'IfStatement', consequent: {type:'EmptyStatement'}, alternate: EMPTY})).to.be.eq EMPTY
+            expect(esutils.ast.trailingStatement({type: 'LabeledStatement', body: EMPTY})).to.be.eq EMPTY
+
+        it 'returns null if node doens\'t have trailing statement', ->
+            expect(esutils.ast.trailingStatement({type: 'DoWhileStatement', body: EMPTY})).to.be.null
+            expect(esutils.ast.trailingStatement({type: 'ReturnStatement' })).to.be.null
+
+    describe 'isProblematicIfStatement', ->
+        it 'returns true if node is problematic if statement', ->
+            expect(esutils.ast.isProblematicIfStatement(
+                type: 'IfStatement'
+                consequent: {
+                    type: 'IfStatement'
+                    consequent: EMPTY
+                }
+                alternate: EMPTY
+            )).to.be.true
+
+            expect(esutils.ast.isProblematicIfStatement(
+                type: 'IfStatement'
+                consequent:
+                    type: 'LabeledStatement'
+                    body:
+                        type: 'IfStatement'
+                        consequent: EMPTY
+                alternate: EMPTY
+            )).to.be.true
+
+            expect(esutils.ast.isProblematicIfStatement(
+                type: 'IfStatement'
+                consequent:
+                    type: 'WithStatement'
+                    body:
+                        type: 'IfStatement'
+                        consequent: EMPTY
+                alternate: EMPTY
+            )).to.be.true
+
+        it 'returns false if node is not problematic if statement', ->
+            expect(esutils.ast.isProblematicIfStatement(
+                type: 'IfStatement'
+                consequent: EMPTY
+                alternate: EMPTY
+            )).to.be.false
+
+            expect(esutils.ast.isProblematicIfStatement(
+                type: 'IfStatement'
+                consequent:
+                    type: 'BlockStatement'
+                    body: [
+                        type: 'IfStatement'
+                        consequent: EMPTY
+                    ]
+                alternate: EMPTY
+            )).to.be.false
+
+            expect(esutils.ast.isProblematicIfStatement(
+                type: 'IfStatement'
+                consequent:
+                    type: 'DoWhileStatement'
+                    body:
+                        type: 'IfStatement'
+                        consequent: EMPTY
+                alternate: EMPTY
+            )).to.be.false
diff --git a/test/code.coffee b/test/code.coffee
new file mode 100644
index 0000000..a626eab
--- /dev/null
+++ b/test/code.coffee
@@ -0,0 +1,183 @@
+# Copyright (C) 2013 Yusuke Suzuki <utatane.tea at gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+'use strict'
+
+expect = require('chai').expect
+esutils = require '../'
+
+describe 'code', ->
+    describe 'isDecimalDigit', ->
+        it 'returns true if provided code is decimal digit', ->
+            for ch in [0..9]
+                expect(esutils.code.isDecimalDigit((ch + '').charCodeAt(0))).to.be.true
+
+        it 'returns false if provided code is not decimal digit', ->
+            for code in ['a'.charCodeAt(0)..'z'.charCodeAt(0)]
+                expect(esutils.code.isDecimalDigit(code)).to.be.false
+
+            for code in ['A'.charCodeAt(0)..'Z'.charCodeAt(0)]
+                expect(esutils.code.isDecimalDigit(code)).to.be.false
+
+    describe 'isHexDigit', ->
+        it 'returns true if provided code is hexadecimal digit', ->
+            for ch in [0..9]
+                expect(esutils.code.isHexDigit((ch + '').charCodeAt(0))).to.be.true
+
+            for code in ['a'.charCodeAt(0)..'f'.charCodeAt(0)]
+                expect(esutils.code.isHexDigit(code)).to.be.true
+
+            for code in ['A'.charCodeAt(0)..'F'.charCodeAt(0)]
+                expect(esutils.code.isHexDigit(code)).to.be.true
+
+        it 'returns false if provided code is not hexadecimal digit', ->
+            for code in ['g'.charCodeAt(0)..'z'.charCodeAt(0)]
+                expect(esutils.code.isHexDigit(code)).to.be.false
+
+            for code in ['G'.charCodeAt(0)..'Z'.charCodeAt(0)]
+                expect(esutils.code.isHexDigit(code)).to.be.false
+
+    describe 'isOctalDigit', ->
+        it 'returns true if provided code is octal digit', ->
+            for ch in [0..7]
+                expect(esutils.code.isOctalDigit((ch + '').charCodeAt(0))).to.be.true
+
+        it 'returns false if provided code is not octal digit', ->
+            for ch in [8..9]
+                expect(esutils.code.isOctalDigit((ch + '').charCodeAt(0))).to.be.false
+
+            for code in ['a'.charCodeAt(0)..'z'.charCodeAt(0)]
+                expect(esutils.code.isOctalDigit(code)).to.be.false
+
+            for code in ['A'.charCodeAt(0)..'Z'.charCodeAt(0)]
+                expect(esutils.code.isOctalDigit(code)).to.be.false
+
+    describe 'isWhiteSpace', ->
+        it 'returns true if provided code is white space', ->
+            codes = [
+                0x0009  # TAB
+                0x000B  # VT
+                0x000C  # FF
+                0x0020  # SP
+                0x00A0  # NBSP
+                0xFEFF  # BOM
+
+                # Zs
+                0x1680
+                0x180E
+                0x2000
+                0x2001
+                0x2002
+                0x2003
+                0x2004
+                0x2005
+                0x2006
+                0x2007
+                0x2008
+                0x2009
+                0x200A
+                0x202F
+                0x205F
+                0x3000
+            ]
+            for code in codes
+                expect(esutils.code.isWhiteSpace(code)).to.be.true
+
+        it 'returns false if provided code is not white space', ->
+            for ch in [0..9]
+                expect(esutils.code.isWhiteSpace((ch + '').charCodeAt(0))).to.be.false
+
+            for code in ['a'.charCodeAt(0)..'z'.charCodeAt(0)]
+                expect(esutils.code.isWhiteSpace(code)).to.be.false
+
+            for code in ['A'.charCodeAt(0)..'Z'.charCodeAt(0)]
+                expect(esutils.code.isWhiteSpace(code)).to.be.false
+
+    describe 'isLineTerminator', ->
+        it 'returns true if provided code is line terminator', ->
+            codes = [
+                0x000A
+                0x000D
+                0x2028
+                0x2029
+            ]
+            for code in codes
+                expect(esutils.code.isLineTerminator(code)).to.be.true
+
+        it 'returns false if provided code is not line terminator', ->
+            for ch in [0..9]
+                expect(esutils.code.isLineTerminator((ch + '').charCodeAt(0))).to.be.false
+
+            for code in ['a'.charCodeAt(0)..'z'.charCodeAt(0)]
+                expect(esutils.code.isLineTerminator(code)).to.be.false
+
+            for code in ['A'.charCodeAt(0)..'Z'.charCodeAt(0)]
+                expect(esutils.code.isLineTerminator(code)).to.be.false
+
+    describe 'isIdentifierStartES5', ->
+        it 'returns true if provided code can be a start of Identifier in ES5', ->
+            characters = ['a', '_', '$', 'ゆ']
+            for code in characters.map((ch) -> ch.charCodeAt(0))
+                expect(esutils.code.isIdentifierStartES5(code)).to.be.true
+
+        it 'returns false if provided code cannot be a start of Identifier in ES5', ->
+            for ch in [0..9]
+                expect(esutils.code.isIdentifierStartES5((ch + '').charCodeAt(0))).to.be.false
+
+    describe 'isIdentifierPartES5', ->
+        it 'returns true if provided code can be a part of Identifier in ES5', ->
+            characters = ['a', '_', '$', 'ゆ']
+            for code in characters.map((ch) -> ch.charCodeAt(0))
+                expect(esutils.code.isIdentifierPartES5(code)).to.be.true
+
+            for ch in [0..9]
+                expect(esutils.code.isIdentifierPartES5((ch + '').charCodeAt(0))).to.be.true
+
+        it 'returns false if provided code cannot be a part of Identifier in ES5', ->
+            expect(esutils.code.isIdentifierPartES5('+'.charCodeAt(0))).to.be.false
+            expect(esutils.code.isIdentifierPartES5('-'.charCodeAt(0))).to.be.false
+
+    describe 'isIdentifierStartES6', ->
+        it 'returns true if provided code can be a start of Identifier in ES6', ->
+            characters = ['a', '_', '$', 'ゆ']
+            for code in characters.map((ch) -> ch.charCodeAt(0))
+                expect(esutils.code.isIdentifierStartES6(code)).to.be.true
+
+        it 'returns false if provided code cannot be a start of Identifier in ES6', ->
+            for ch in [0..9]
+                expect(esutils.code.isIdentifierStartES6((ch + '').charCodeAt(0))).to.be.false
+
+    describe 'isIdentifierPartES6', ->
+        it 'returns true if provided code can be a part of Identifier in ES6', ->
+            characters = ['a', '_', '$', 'ゆ']
+            for code in characters.map((ch) -> ch.charCodeAt(0))
+                expect(esutils.code.isIdentifierPartES6(code)).to.be.true
+
+            for ch in [0..9]
+                expect(esutils.code.isIdentifierPartES6((ch + '').charCodeAt(0))).to.be.true
+
+        it 'supports astral symbols', ->
+            expect(esutils.code.isIdentifierPartES6(0xE01D5)).to.be.true
+
+        it 'returns false if provided code cannot be a part of Identifier in ES6', ->
+            expect(esutils.code.isIdentifierPartES6('+'.charCodeAt(0))).to.be.false
+            expect(esutils.code.isIdentifierPartES6('-'.charCodeAt(0))).to.be.false
diff --git a/test/keyword.coffee b/test/keyword.coffee
new file mode 100644
index 0000000..9fef055
--- /dev/null
+++ b/test/keyword.coffee
@@ -0,0 +1,400 @@
+# Copyright (C) 2013 Yusuke Suzuki <utatane.tea at gmail.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#   * Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer.
+#   * Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+'use strict'
+
+expect = require('chai').expect
+esutils = require '../'
+
+KW = [
+    'if'
+    'in'
+    'do'
+    'var'
+    'for'
+    'new'
+    'try'
+    'this'
+    'else'
+    'case'
+    'void'
+    'with'
+    'enum'
+    'while'
+    'break'
+    'catch'
+    'throw'
+    'const'
+    'class'
+    'super'
+    'return'
+    'typeof'
+    'delete'
+    'switch'
+    'export'
+    'import'
+    'default'
+    'finally'
+    'extends'
+    'function'
+    'continue'
+    'debugger'
+    'instanceof'
+]
+
+SRW = [
+    'implements'
+    'interface'
+    'package'
+    'private'
+    'protected'
+    'public'
+    'static'
+    'let'
+]
+
+describe 'keyword', ->
+    describe 'isKeywordES6', ->
+        it 'returns true if provided string is keyword under non-strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isKeywordES6(word, no)).to.be.true
+
+            expect(esutils.keyword.isKeywordES6('yield', no)).to.be.true
+
+        it 'returns false if provided string is not keyword under non-strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isKeywordES6(word, no)).to.be.false
+
+            for word in SRW
+                expect(esutils.keyword.isKeywordES6(word, no)).to.be.false
+
+        it 'returns true if provided string is keyword under strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isKeywordES6(word, yes)).to.be.true
+
+            expect(esutils.keyword.isKeywordES6('yield', yes)).to.be.true
+
+            for word in SRW
+                expect(esutils.keyword.isKeywordES6(word, yes)).to.be.true
+
+
+        it 'returns false if provided string is not keyword under strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isKeywordES6(word, yes)).to.be.false
+
+
+    describe 'isKeywordES5', ->
+        it 'returns true if provided string is keyword under non-strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isKeywordES5(word, no)).to.be.true
+
+        it 'returns false if provided string is not keyword under non-strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isKeywordES5(word, no)).to.be.false
+
+            for word in SRW
+                expect(esutils.keyword.isKeywordES5(word, no)).to.be.false
+
+            expect(esutils.keyword.isKeywordES5('yield', no)).to.be.false
+
+        it 'returns true if provided string is keyword under strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isKeywordES5(word, yes)).to.be.true
+
+            expect(esutils.keyword.isKeywordES5('yield', yes)).to.be.true
+
+            for word in SRW
+                expect(esutils.keyword.isKeywordES5(word, yes)).to.be.true
+
+
+        it 'returns false if provided string is not keyword under strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isKeywordES5(word, yes)).to.be.false
+
+
+    describe 'isReservedWordES6', ->
+        it 'returns true for null/boolean values', ->
+            expect(esutils.keyword.isReservedWordES6('null', no)).to.be.true
+            expect(esutils.keyword.isReservedWordES6('null', yes)).to.be.true
+            expect(esutils.keyword.isReservedWordES6('true', no)).to.be.true
+            expect(esutils.keyword.isReservedWordES6('true', yes)).to.be.true
+            expect(esutils.keyword.isReservedWordES6('false', no)).to.be.true
+            expect(esutils.keyword.isReservedWordES6('false', yes)).to.be.true
+
+        # isReservedWordES6 has the same properties as isKeywordES6
+
+        it 'returns true if provided string is keyword under non-strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isReservedWordES6(word, no)).to.be.true
+
+            expect(esutils.keyword.isReservedWordES6('yield', no)).to.be.true
+
+        it 'returns false if provided string is not keyword under non-strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isReservedWordES6(word, no)).to.be.false
+
+            for word in SRW
+                expect(esutils.keyword.isReservedWordES6(word, no)).to.be.false
+
+        it 'returns true if provided string is keyword under strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isReservedWordES6(word, yes)).to.be.true
+
+            expect(esutils.keyword.isReservedWordES6('yield', yes)).to.be.true
+
+            for word in SRW
+                expect(esutils.keyword.isReservedWordES6(word, yes)).to.be.true
+
+
+        it 'returns false if provided string is not keyword under strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isReservedWordES6(word, yes)).to.be.false
+
+
+    describe 'isReservedWordES5', ->
+        it 'returns true for null/boolean values', ->
+            expect(esutils.keyword.isReservedWordES5('null', no)).to.be.true
+            expect(esutils.keyword.isReservedWordES5('null', yes)).to.be.true
+            expect(esutils.keyword.isReservedWordES5('true', no)).to.be.true
+            expect(esutils.keyword.isReservedWordES5('true', yes)).to.be.true
+            expect(esutils.keyword.isReservedWordES5('false', no)).to.be.true
+            expect(esutils.keyword.isReservedWordES5('false', yes)).to.be.true
+
+        # isReservedWordES5 has the same properties as isKeywordES5
+
+        it 'returns true if provided string is keyword under non-strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isReservedWordES5(word, no)).to.be.true
+
+        it 'returns false if provided string is not keyword under non-strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isReservedWordES5(word, no)).to.be.false
+
+            for word in SRW
+                expect(esutils.keyword.isReservedWordES5(word, no)).to.be.false
+
+            expect(esutils.keyword.isReservedWordES5('yield', no)).to.be.false
+
+        it 'returns true if provided string is keyword under strict mode', ->
+            for word in KW
+                expect(esutils.keyword.isReservedWordES5(word, yes)).to.be.true
+
+            expect(esutils.keyword.isReservedWordES5('yield', yes)).to.be.true
+
+            for word in SRW
+                expect(esutils.keyword.isReservedWordES5(word, yes)).to.be.true
+
+
+        it 'returns false if provided string is not keyword under strict mode', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isReservedWordES5(word, yes)).to.be.false
+
+
+    describe 'isRestrictedWord', ->
+        it 'returns true if provided string is "eval" or "arguments"', ->
+            expect(esutils.keyword.isRestrictedWord('eval')).to.be.true
+            expect(esutils.keyword.isRestrictedWord('arguments')).to.be.true
+
+        it 'returns false if provided string is not "eval" or "arguments"', ->
+            words = [
+                'hello'
+                '20'
+                '$'
+                'ゆゆ式'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isRestrictedWord(word)).to.be.false
+
+
+    describe 'isIdentifierName', ->
+        it 'returns false if provided string is empty', ->
+            expect(esutils.keyword.isIdentifierNameES5('')).to.be.false
+            expect(esutils.keyword.isIdentifierNameES6('')).to.be.false
+
+        it 'returns true if provided string is IdentifierName', ->
+            words = [
+                'hello'
+                '$'
+                'ゆゆ式'
+                '$20'
+                'hello20'
+                '_'
+                'if'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isIdentifierNameES5(word)).to.be.true
+                expect(esutils.keyword.isIdentifierNameES6(word)).to.be.true
+
+
+        it 'returns false if provided string is not IdentifierName', ->
+            words = [
+                '+hello'
+                '0$'
+                '-ゆゆ式'
+                '#_'
+                '_#'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isIdentifierNameES5(word)).to.be.false
+                expect(esutils.keyword.isIdentifierNameES6(word)).to.be.false
+
+        it 'supports astral symbols', ->
+            expect(esutils.keyword.isIdentifierNameES6('x\uDB40\uDDD5')).to.be.true
+
+
+    describe 'isIdentifierES5', ->
+        it 'returns false if provided string is empty', ->
+            expect(esutils.keyword.isIdentifierES5('')).to.be.false
+
+        it 'returns true if provided string is Identifier', ->
+            words = [
+                'hello'
+                '$'
+                'ゆゆ式'
+                '$20'
+                'hello20'
+                '_'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isIdentifierES5(word)).to.be.true
+
+            expect(esutils.keyword.isIdentifierES5('yield', no)).to.be.true
+            expect(esutils.keyword.isIdentifierES5('let', no)).to.be.true
+
+        it 'returns false if provided string is not Identifier', ->
+            words = [
+                '+hello'
+                '0$'
+                '-ゆゆ式'
+                '#_'
+                '_#'
+                'if'
+                'null'
+                'true'
+                'false'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isIdentifierES5(word)).to.be.false
+
+            expect(esutils.keyword.isIdentifierES5('yield', yes)).to.be.false
+            expect(esutils.keyword.isIdentifierES5('let', yes)).to.be.false
+
+
+    describe 'isIdentifierES6', ->
+        it 'returns false if provided string is empty', ->
+            expect(esutils.keyword.isIdentifierES6('')).to.be.false
+
+        it 'returns true if provided string is Identifier', ->
+            words = [
+                'hello'
+                '$'
+                'ゆゆ式'
+                '$20'
+                'hello20'
+                '_'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isIdentifierES6(word)).to.be.true
+
+            expect(esutils.keyword.isIdentifierES6('let', no)).to.be.true
+
+        it 'returns false if provided string is not Identifier', ->
+            words = [
+                '+hello'
+                '0$'
+                '-ゆゆ式'
+                '#_'
+                '_#'
+                'if'
+                'null'
+                'true'
+                'false'
+            ]
+
+            for word in words
+                expect(esutils.keyword.isIdentifierES6(word)).to.be.false
+
+            expect(esutils.keyword.isIdentifierES6('yield', no)).to.be.false
+            expect(esutils.keyword.isIdentifierES6('yield', yes)).to.be.false
+            expect(esutils.keyword.isIdentifierES6('let', yes)).to.be.false
diff --git a/tools/generate-identifier-regex.js b/tools/generate-identifier-regex.js
new file mode 100644
index 0000000..01c8153
--- /dev/null
+++ b/tools/generate-identifier-regex.js
@@ -0,0 +1,87 @@
+// Based on https://gist.github.com/mathiasbynens/6334847 by @mathias
+'use strict';
+
+var regenerate = require('regenerate');
+
+// Which Unicode version should be used?
+var version = '7.0.0';
+
+// Set up a shorthand function to import Unicode data.
+var get = function(what) {
+    return require('unicode-' + version + '/' + what + '/code-points');
+};
+
+// Get the Unicode categories needed to construct the ES5 regex.
+var Lu = get('categories/Lu');
+var Ll = get('categories/Ll');
+var Lt = get('categories/Lt');
+var Lm = get('categories/Lm');
+var Lo = get('categories/Lo');
+var Nl = get('categories/Nl');
+var Mn = get('categories/Mn');
+var Mc = get('categories/Mc');
+var Nd = get('categories/Nd');
+var Pc = get('categories/Pc');
+
+var es5regexes = (function() { // ES 5.1
+    // http://mathiasbynens.be/notes/javascript-identifiers#valid-identifier-names
+    var identifierStart = regenerate()
+        .add(Lu, Ll, Lt, Lm, Lo, Nl)
+        .removeRange(0x010000, 0x10FFFF) // remove astral symbols
+        .removeRange(0x0, 0x7F); // remove ASCII symbols (esutils-specific)
+    var identifierStartCodePoints = identifierStart.toArray();
+    var identifierPart = regenerate(identifierStartCodePoints)
+        .add('\u200C', '\u200D', Mn, Mc, Nd, Pc)
+        .removeRange(0x010000, 0x10FFFF) // remove astral symbols
+        .removeRange(0x0, 0x7F); // remove ASCII symbols (esutils-specific)
+    return {
+        'NonAsciiIdentifierStart': '/' + identifierStart + '/',
+        'NonAsciiIdentifierPart': '/' + identifierPart + '/',
+    };
+}());
+
+// Get the Unicode properties needed to construct the ES6 regex.
+var ID_Start = get('properties/ID_Start');
+var ID_Continue = get('properties/ID_Continue');
+var Other_ID_Start = get('properties/Other_ID_Start');
+
+var es6regexes = (function() {
+    // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-identifier-names-static-semantics-early-errors
+    // http://unicode.org/reports/tr31/#Default_Identifier_Syntax
+    // https://bugs.ecmascript.org/show_bug.cgi?id=2717#c0
+    var identifierStart = regenerate(ID_Start)
+        // Note: this already includes `Other_ID_Start`. http://git.io/wRCAfQ
+        .removeRange(0x0, 0x7F); // remove ASCII symbols (esutils-specific)
+    var identifierPart = regenerate(ID_Continue)
+        // Note: `ID_Continue` already includes `Other_ID_Continue`. http://git.io/wRCAfQ
+        .add(Other_ID_Start)
+        .add('\u200C', '\u200D')
+        .removeRange(0x0, 0x7F); // remove ASCII symbols (esutils-specific)
+
+    return {
+        'NonAsciiIdentifierStart': '/' + identifierStart + '/',
+        'NonAsciiIdentifierPart': '/' + identifierPart + '/',
+    };
+}());
+
+console.log(
+    '// ECMAScript 5.1/Unicode v%s NonAsciiIdentifierStart:\n%s\n',
+    version,
+    es5regexes.NonAsciiIdentifierStart
+);
+console.log(
+    '// ECMAScript 5.1/Unicode v%s NonAsciiIdentifierPart:\n%s\n',
+    version,
+    es5regexes.NonAsciiIdentifierPart
+);
+
+console.log(
+    '// ECMAScript 6/Unicode v%s NonAsciiIdentifierStart:\n%s\n',
+    version,
+    es6regexes.NonAsciiIdentifierStart
+);
+console.log(
+    '// ECMAScript 6/Unicode v%s NonAsciiIdentifierPart:\n%s',
+    version,
+    es6regexes.NonAsciiIdentifierPart
+);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-esutils.git



More information about the Pkg-javascript-commits mailing list