diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4f5d9ff..a9721fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+### Update to JSLint 2019-01-31
+* **lib/jslint-latest.js**: update to JSLint 2019-01-31.
+* **lib/jslint-2019-01-31.js**: add JSLint 2019-01-31.
+* **README.md**: add note about upgrading to JSLint 2019-01-31.
+* **CHANGELOG.md**: add notes about upgrading to JSLint 2019-01-31.
+
## [0.12.1](https://github.com/reid/node-jslint/compare/v0.12.0...v0.12.1) (2019-01-28)
@@ -48,7 +54,7 @@ you must use `--edition=es6`.
2015-07-29 Sam Mikes
* lib/jslint-es6.js: latest jslint from upstream
- * lib/nodelint.js, test/regression.js: correctly report edition for post-es6 jslints,
+ * lib/nodelint.js, test/regression.js: correctly report edition for post-es6 jslints,
thanks to @bryanjhv for the bug report and fix
2015-02-19 Sam Mikes
@@ -68,7 +74,7 @@ you must use `--edition=es6`.
2104-04-13 Sam Mikes
* lib/linter.js: Fix issue #88 - support user-specified config file, support
- jslint.conf and .jslint.conf in addition to jslintrc, .jslintrc
+ jslint.conf and .jslint.conf in addition to jslintrc, .jslintrc
2014-01-30 Sam Mikes
diff --git a/README.md b/README.md
index 6880989..efc6772 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ Easily use [JSLint][] from the command line.
## What's New
-Added latest jslint, 2018-01-27.
+Added latest jslint, 2019-01-31.
Version 0.12.0 contains the latest jslint-es6
diff --git a/lib/collectorstream.js b/lib/collectorstream.js
index 316d623..2e3584c 100644
--- a/lib/collectorstream.js
+++ b/lib/collectorstream.js
@@ -6,11 +6,11 @@
// Released under modified MIT/BSD 3-clause license
// See LICENSE for details.
-'use strict';
+"use strict";
-var util = require('util'),
- Transform = require('./stream').Transform,
- CollectorStream;
+var util = require('util');
+var Transform = require('./stream').Transform;
+var CollectorStream;
CollectorStream = function CollectorStream_constructor(options) {
if (!(this instanceof CollectorStream)) {
diff --git a/lib/color.js b/lib/color.js
index d58993f..3e57bc4 100644
--- a/lib/color.js
+++ b/lib/color.js
@@ -1,10 +1,10 @@
function color(code, string) {
- 'use strict';
+ "use strict";
return "\u001b[" + code + "m" + string + "\u001b[0m";
}
function factory(code) {
- 'use strict';
+ "use strict";
return function (string) {
return color(code, string);
};
diff --git a/lib/fileopener.js b/lib/fileopener.js
index e92e120..446c8c2 100644
--- a/lib/fileopener.js
+++ b/lib/fileopener.js
@@ -1,8 +1,8 @@
-'use strict';
+"use strict";
-var util = require('util'),
- Transform = require('./stream').Transform,
- fs = require('fs');
+var util = require("util");
+var Transform = require("./stream").Transform;
+var fs = require("fs");
function FileOpener() {
Transform.call(this, {objectMode: true});
@@ -26,4 +26,3 @@ function FileOpener_transform(file, ignore, callback) {
FileOpener.prototype._transform = FileOpener_transform;
module.exports = FileOpener;
-
diff --git a/lib/jslint-2019-01-31.js b/lib/jslint-2019-01-31.js
new file mode 100644
index 0000000..e7b05db
--- /dev/null
+++ b/lib/jslint-2019-01-31.js
@@ -0,0 +1,4947 @@
+// jslint.js
+// 2019-01-31
+// Copyright (c) 2015 Douglas Crockford (www.JSLint.com)
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+
+// The Software shall be used for Good, not Evil.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+// jslint(source, option_object, global_array) is a function that takes 3
+// arguments. The second two arguments are optional.
+
+// source A text to analyze, a string or an array of strings.
+// option_object An object whose keys correspond to option names.
+// global_array An array of strings containing global variables that
+// the file is allowed readonly access.
+
+// jslint returns an object containing its results. The object contains a lot
+// of valuable information. It can be used to generate reports. The object
+// contains:
+
+// directives: an array of directive comment tokens.
+// edition: the version of JSLint that did the analysis.
+// exports: the names exported from the module.
+// froms: an array of strings representing each of the imports.
+// functions: an array of objects that represent all of the functions
+// declared in the file.
+// global: an object representing the global object. Its .context property
+// is an object containing a property for each global variable.
+// id: "(JSLint)"
+// json: true if the file is a JSON text.
+// lines: an array of strings, the source.
+// module: true if an import or export statement was used.
+// ok: true if no warnings were generated. This is what you want.
+// option: the option argument.
+// property: a property object.
+// stop: true if JSLint was unable to finish. You don't want this.
+// tokens: an array of objects representing the tokens in the file.
+// tree: the token objects arranged in a tree.
+// warnings: an array of warning objects. A warning object can contain:
+// name: "JSLintError"
+// column: A column number in the file.
+// line: A line number in the file.
+// code: A warning code string.
+// message: The warning message string.
+// a: Exhibit A.
+// b: Exhibit B.
+// c: Exhibit C.
+// d: Exhibit D.
+
+// jslint works in several phases. In any of these phases, errors might be
+// found. Sometimes JSLint is able to recover from an error and continue
+// parsing. In some cases, it cannot and will stop early. If that should happen,
+// repair your code and try again.
+
+// Phases:
+
+// 1. If the source is a single string, split it into an array of strings.
+// 2. Turn the source into an array of tokens.
+// 3. Furcate the tokens into a parse tree.
+// 4. Walk the tree, traversing all of the nodes of the tree. It is a
+// recursive traversal. Each node may be processed on the way down
+// (preaction) and on the way up (postaction).
+// 5. Check the whitespace between the tokens.
+
+// jslint can also examine JSON text. It decides that a file is JSON text if
+// the first token is "[" or "{". Processing of JSON text is much simpler than
+// the processing of JavaScript programs. Only the first three phases are
+// required.
+
+// WARNING: JSLint will hurt your feelings.
+
+/*property
+ a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get,
+ bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block,
+ body, browser, c, calls, catch, charCodeAt, closer, closure, code, column,
+ concat, constant, context, convert, couch, create, d, dead, default, devel,
+ directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else,
+ empty_block, escape_mega, eval, every, expected_a, expected_a_at_b_c,
+ expected_a_b, expected_a_b_from_c_d, expected_a_before_b,
+ expected_a_next_at_b, expected_digits_after_a, expected_four_digits,
+ expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a,
+ expected_space_a_b, expected_statements_a, expected_string_a,
+ expected_type_string_a, exports, expression, extra, finally, flag, for,
+ forEach, free, freeze, freeze_exports, from, froms, fud, fudge,
+ function_in_loop, functions, g, getset, global, i, id, identifier, import,
+ inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys,
+ label, label_a, lbp, led, length, level, line, lines, live, long, loop, m,
+ margin, match, message, misplaced_a, misplaced_directive_a, missing_browser,
+ missing_m, module, naked_block, name, names, nested_comment, new, node,
+ not_label_a, nr, nud, number_isNaN, ok, open, opening, option,
+ out_of_scope_a, parameters, parent, pop, property, push, quote,
+ redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search,
+ shebang, signature, single, slice, some, sort, split, startsWith, statement,
+ stop, subscript_a, switch, test, this, thru, toString, todo_comment,
+ tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment,
+ unclosed_mega, unclosed_string, undeclared_a, unexpected_a,
+ unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a,
+ unexpected_char_a, unexpected_comment, unexpected_directive_a,
+ unexpected_expression_a, unexpected_label_a, unexpected_parens,
+ unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space,
+ unexpected_typeof_a, uninitialized_a, unreachable_a,
+ unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces,
+ used, value, var_loop, var_switch, variable, warning, warnings,
+ weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white,
+ wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary,
+ wrapped, writable, y
+*/
+
+function empty() {
+
+// The empty function produces a new empty object that inherits nothing. This is
+// much better than '{}' because confusions around accidental method names like
+// 'constructor' are completely avoided.
+
+ return Object.create(null);
+}
+
+function populate(array, object = empty(), value = true) {
+
+// Augment an object by taking property names from an array of strings.
+
+ array.forEach(function (name) {
+ object[name] = value;
+ });
+ return object;
+}
+
+const allowed_option = {
+
+// These are the options that are recognized in the option object or that may
+// appear in a /*jslint*/ directive. Most options will have a boolean value,
+// usually true. Some options will also predefine some number of global
+// variables.
+
+ bitwise: true,
+ browser: [
+ "caches", "clearInterval", "clearTimeout", "document", "DOMException",
+ "Element", "Event", "event", "FileReader", "FormData", "history",
+ "localStorage", "location", "MutationObserver", "name", "navigator",
+ "screen", "sessionStorage", "setInterval", "setTimeout", "Storage",
+ "TextDecoder", "TextEncoder", "URL", "window", "Worker",
+ "XMLHttpRequest"
+ ],
+ couch: [
+ "emit", "getRow", "isArray", "log", "provides", "registerType",
+ "require", "send", "start", "sum", "toJSON"
+ ],
+ convert: true,
+ devel: [
+ "alert", "confirm", "console", "prompt"
+ ],
+ eval: true,
+ for: true,
+ fudge: true,
+ getset: true,
+ long: true,
+ node: [
+ "Buffer", "clearImmediate", "clearInterval", "clearTimeout",
+ "console", "exports", "module", "process", "require",
+ "setImmediate", "setInterval", "setTimeout", "TextDecoder",
+ "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename"
+ ],
+ single: true,
+ this: true,
+ white: true
+};
+
+const anticondition = populate([
+ "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%",
+ "typeof", "(number)", "(string)"
+]);
+
+// These are the bitwise operators.
+
+const bitwiseop = populate([
+ "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=",
+ ">>>", ">>>="
+]);
+
+const escapeable = populate([
+ "\\", "/", "`", "b", "f", "n", "r", "t"
+]);
+
+const opener = {
+
+// The open and close pairs.
+
+ "(": ")", // paren
+ "[": "]", // bracket
+ "{": "}", // brace
+ "${": "}" // mega
+};
+
+// The relational operators.
+
+const relationop = populate([
+ "!=", "!==", "==", "===", "<", "<=", ">", ">="
+]);
+
+// This is the set of infix operators that require a space on each side.
+
+const spaceop = populate([
+ "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/",
+ "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=",
+ ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
+]);
+
+const standard = [
+
+// These are the globals that are provided by the language standard.
+
+ "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI",
+ "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error",
+ "EvalError", "Float32Array", "Float64Array", "Generator",
+ "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl",
+ "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat",
+ "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp",
+ "Set", "String", "Symbol", "SyntaxError", "System", "TypeError",
+ "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array",
+ "URIError", "WeakMap", "WeakSet"
+];
+
+const bundle = {
+
+// The bundle contains the raw text messages that are generated by jslint. It
+// seems that they are all error messages and warnings. There are no "Atta
+// boy!" or "You are so awesome!" messages. There is no positive reinforcement
+// or encouragement. This relentless negativity can undermine self-esteem and
+// wound the inner child. But if you accept it as sound advice rather than as
+// personal criticism, it can make your programs better.
+
+ and: "The '&&' subexpression should be wrapped in parens.",
+ bad_assignment_a: "Bad assignment to '{a}'.",
+ bad_directive_a: "Bad directive '{a}'.",
+ bad_get: "A get function takes no parameters.",
+ bad_module_name_a: "Bad module name '{a}'.",
+ bad_option_a: "Bad option '{a}'.",
+ bad_property_a: "Bad property name '{a}'.",
+ bad_set: "A set function takes one parameter.",
+ duplicate_a: "Duplicate '{a}'.",
+ empty_block: "Empty block.",
+ escape_mega: "Unexpected escapement in mega literal.",
+ expected_a: "Expected '{a}'.",
+ expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
+ expected_a_b: "Expected '{a}' and instead saw '{b}'.",
+ expected_a_b_from_c_d: (
+ "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'."
+ ),
+ expected_a_before_b: "Expected '{a}' before '{b}'.",
+ expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.",
+ expected_digits_after_a: "Expected digits after '{a}'.",
+ expected_four_digits: "Expected four digits after '\\u'.",
+ expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
+ expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
+ expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
+ expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
+ expected_statements_a: "Expected statements before '{a}'.",
+ expected_string_a: "Expected a string and instead saw '{a}'.",
+ expected_type_string_a: "Expected a type string and instead saw '{a}'.",
+ freeze_exports: (
+ "Expected 'Object.freeze('. All export values should be frozen."
+ ),
+ function_in_loop: "Don't make functions within a loop.",
+ infix_in: (
+ "Unexpected 'in'. Compare with undefined, "
+ + "or use the hasOwnProperty method instead."
+ ),
+ label_a: "'{a}' is a statement label.",
+ misplaced_a: "Place '{a}' at the outermost level.",
+ misplaced_directive_a: (
+ "Place the '/*{a}*/' directive before the first statement."
+ ),
+ missing_browser: "/*global*/ requires the Assume a browser option.",
+ missing_m: "Expected 'm' flag on a multiline regular expression.",
+ naked_block: "Naked block.",
+ nested_comment: "Nested comment.",
+ not_label_a: "'{a}' is not a label.",
+ number_isNaN: "Use Number.isNaN function to compare with NaN.",
+ out_of_scope_a: "'{a}' is out of scope.",
+ redefinition_a_b: "Redefinition of '{a}' from line {b}.",
+ required_a_optional_b: (
+ "Required parameter '{a}' after optional parameter '{b}'."
+ ),
+ reserved_a: "Reserved name '{a}'.",
+ subscript_a: "['{a}'] is better written in dot notation.",
+ todo_comment: "Unexpected TODO comment.",
+ too_long: "Line is longer than 80 characters.",
+ too_many_digits: "Too many digits.",
+ unclosed_comment: "Unclosed comment.",
+ unclosed_mega: "Unclosed mega literal.",
+ unclosed_string: "Unclosed string.",
+ undeclared_a: "Undeclared '{a}'.",
+ unexpected_a: "Unexpected '{a}'.",
+ unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
+ unexpected_a_before_b: "Unexpected '{a}' before '{b}'.",
+ unexpected_at_top_level_a: "Expected '{a}' to be in a function.",
+ unexpected_char_a: "Unexpected character '{a}'.",
+ unexpected_comment: "Unexpected comment.",
+ unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
+ unexpected_expression_a: (
+ "Unexpected expression '{a}' in statement position."
+ ),
+ unexpected_label_a: "Unexpected label '{a}'.",
+ unexpected_parens: "Don't wrap function literals in parens.",
+ unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
+ unexpected_statement_a: (
+ "Unexpected statement '{a}' in expression position."
+ ),
+ unexpected_trailing_space: "Unexpected trailing space.",
+ unexpected_typeof_a: (
+ "Unexpected 'typeof'. Use '===' to compare directly with {a}."
+ ),
+ uninitialized_a: "Uninitialized '{a}'.",
+ unreachable_a: "Unreachable '{a}'.",
+ unregistered_property_a: "Unregistered property name '{a}'.",
+ unsafe: "Unsafe character '{a}'.",
+ unused_a: "Unused '{a}'.",
+ use_double: "Use double quotes, not single quotes.",
+ use_open: (
+ "Wrap a ternary expression in parens, "
+ + "with a line break after the left paren."
+ ),
+ use_spaces: "Use spaces, not tabs.",
+ var_loop: "Don't declare variables in a loop.",
+ var_switch: "Don't declare variables in a switch.",
+ weird_condition_a: "Weird condition '{a}'.",
+ weird_expression_a: "Weird expression '{a}'.",
+ weird_loop: "Weird loop.",
+ weird_relation_a: "Weird relation '{a}'.",
+ wrap_condition: "Wrap the condition in parens.",
+ wrap_immediate: (
+ "Wrap an immediate function invocation in parentheses to assist "
+ + "the reader in understanding that the expression is the result "
+ + "of a function, and not the function itself."
+ ),
+ wrap_parameter: "Wrap the parameter in parens.",
+ wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
+ wrap_unary: "Wrap the unary expression in parens."
+};
+
+// Regular expression literals:
+
+// supplant {variables}
+const rx_supplant = /\{([^{}]*)\}/g;
+// carriage return, carriage return linefeed, or linefeed
+const rx_crlf = /\n|\r\n?/;
+// unsafe characters that are silently deleted by one or more browsers
+const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
+// identifier
+const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
+const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/;
+const rx_bad_property = /^_|\$|Sync\$|_$/;
+// star slash
+const rx_star_slash = /\*\//;
+// slash star
+const rx_slash_star = /\/\*/;
+// slash star or ending slash
+const rx_slash_star_or_slash = /\/\*|\/$/;
+// uncompleted work comment
+const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/;
+// tab
+const rx_tab = /\t/g;
+// directive
+const rx_directive = /^(jslint|property|global)\s+(.*)$/;
+const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/;
+// token (sorry it is so long)
+const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<=?|!(?:!|==?)?|(0|[1-9][0-9]*))(.*)$/;
+const rx_digits = /^([0-9]+)(.*)$/;
+const rx_hexs = /^([0-9a-fA-F]+)(.*)$/;
+const rx_octals = /^([0-7]+)(.*)$/;
+const rx_bits = /^([01]+)(.*)$/;
+// mega
+const rx_mega = /[`\\]|\$\{/;
+// JSON number
+const rx_JSON_number = /^-?\d+(?:\.\d*)?(?:e[\-+]?\d+)?$/i;
+// initial cap
+const rx_cap = /^[A-Z]/;
+
+function is_letter(string) {
+ return (
+ (string >= "a" && string <= "z\uffff")
+ || (string >= "A" && string <= "Z\uffff")
+ );
+}
+
+function supplant(string, object) {
+ return string.replace(rx_supplant, function (found, filling) {
+ const replacement = object[filling];
+ return (
+ replacement !== undefined
+ ? replacement
+ : found
+ );
+ });
+}
+
+let anon; // The guessed name for anonymous functions.
+let blockage; // The current block.
+let block_stack; // The stack of blocks.
+let declared_globals; // The object containing the global declarations.
+let directives; // The directive comments.
+let directive_mode; // true if directives are still allowed.
+let early_stop; // true if JSLint cannot finish.
+let exports; // The exported names and values.
+let froms; // The array collecting all import-from strings.
+let fudge; // true if the natural numbers start with 1.
+let functionage; // The current function.
+let functions; // The array containing all of the functions.
+let global; // The global object; the outermost context.
+let json_mode; // true if parsing JSON.
+let lines; // The array containing source lines.
+let mega_mode; // true if currently parsing a megastring literal.
+let module_mode; // true if import or export was used.
+let next_token; // The next token to be examined in the parse.
+let option; // The options parameter.
+let property; // The object containing the tallied property names.
+let shebang; // true if a #! was seen on the first line.
+let stack; // The stack of functions.
+let syntax; // The object containing the parser.
+let token; // The current token being examined in the parse.
+let token_nr; // The number of the next token.
+let tokens; // The array of tokens.
+let tenure; // The predefined property registry.
+let tree; // The abstract parse tree.
+let var_mode; // "var" if using var; "let" if using let.
+let warnings; // The array collecting all generated warnings.
+
+// Error reportage functions:
+
+function artifact(the_token) {
+
+// Return a string representing an artifact.
+
+ if (the_token === undefined) {
+ the_token = next_token;
+ }
+ return (
+ (the_token.id === "(string)" || the_token.id === "(number)")
+ ? String(the_token.value)
+ : the_token.id
+ );
+}
+
+function artifact_line(the_token) {
+
+// Return the fudged line number of an artifact.
+
+ if (the_token === undefined) {
+ the_token = next_token;
+ }
+ return the_token.line + fudge;
+}
+
+function artifact_column(the_token) {
+
+// Return the fudged column number of an artifact.
+
+ if (the_token === undefined) {
+ the_token = next_token;
+ }
+ return the_token.from + fudge;
+}
+
+function warn_at(code, line, column, a, b, c, d) {
+
+// Report an error at some line and column of the program. The warning object
+// resembles an exception.
+
+ const warning = { // ~~
+ name: "JSLintError",
+ column,
+ line,
+ code
+ };
+ if (a !== undefined) {
+ warning.a = a;
+ }
+ if (b !== undefined) {
+ warning.b = b;
+ }
+ if (c !== undefined) {
+ warning.c = c;
+ }
+ if (d !== undefined) {
+ warning.d = d;
+ }
+ warning.message = supplant(bundle[code] || code, warning);
+ warnings.push(warning);
+ return warning;
+}
+
+function stop_at(code, line, column, a, b, c, d) {
+
+// Same as warn_at, except that it stops the analysis.
+
+ throw warn_at(code, line, column, a, b, c, d);
+}
+
+function warn(code, the_token, a, b, c, d) {
+
+// Same as warn_at, except the warning will be associated with a specific token.
+// If there is already a warning on this token, suppress the new one. It is
+// likely that the first warning will be the most meaningful.
+
+ if (the_token === undefined) {
+ the_token = next_token;
+ }
+ if (the_token.warning === undefined) {
+ the_token.warning = warn_at(
+ code,
+ the_token.line,
+ the_token.from,
+ a || artifact(the_token),
+ b,
+ c,
+ d
+ );
+ return the_token.warning;
+ }
+}
+
+function stop(code, the_token, a, b, c, d) {
+
+// Similar to warn and stop_at. If the token already had a warning, that
+// warning will be replaced with this new one. It is likely that the stopping
+// warning will be the more meaningful.
+
+ if (the_token === undefined) {
+ the_token = next_token;
+ }
+ delete the_token.warning;
+ throw warn(code, the_token, a, b, c, d);
+}
+
+// Tokenize:
+
+function tokenize(source) {
+
+// tokenize takes a source and produces from it an array of token objects.
+// JavaScript is notoriously difficult to tokenize because of the horrible
+// interactions between automatic semicolon insertion, regular expression
+// literals, and now megastring literals. JSLint benefits from eliminating
+// automatic semicolon insertion and nested megastring literals, which allows
+// full tokenization to precede parsing.
+
+// If the source is not an array, then it is split into lines at the
+// carriage return/linefeed.
+
+ lines = (
+ Array.isArray(source)
+ ? source
+ : source.split(rx_crlf)
+ );
+ tokens = [];
+
+ let char; // a popular character
+ let column = 0; // the column number of the next character
+ let first; // the first token
+ let from; // the starting column number of the token
+ let line = -1; // the line number of the next character
+ let nr = 0; // the next token number
+ let previous = global; // the previous token including comments
+ let prior = global; // the previous token excluding comments
+ let mega_from; // the starting column of megastring
+ let mega_line; // the starting line of megastring
+ let regexp_seen; // regular expression literal seen on this line
+ let snippet; // a piece of string
+ let source_line = ""; // the remaining line source string
+ let whole_line = ""; // the whole line source string
+
+ if (lines[0].startsWith("#!")) {
+ line = 0;
+ shebang = true;
+ }
+
+ function next_line() {
+
+// Put the next line of source in source_line. If the line contains tabs,
+// replace them with spaces and give a warning. Also warn if the line contains
+// unsafe characters or is too damn long.
+
+ let at;
+ if (
+ !option.long
+ && whole_line.length > 80
+ && !json_mode
+ && first
+ && !regexp_seen
+ ) {
+ warn_at("too_long", line, 80);
+ }
+ column = 0;
+ line += 1;
+ regexp_seen = false;
+ source_line = lines[line];
+ whole_line = source_line || "";
+ if (source_line !== undefined) {
+ at = source_line.search(rx_tab);
+ if (at >= 0) {
+ if (!option.white) {
+ warn_at("use_spaces", line, at + 1);
+ }
+ source_line = source_line.replace(rx_tab, " ");
+ }
+ at = source_line.search(rx_unsafe);
+ if (at >= 0) {
+ warn_at(
+ "unsafe",
+ line,
+ column + at,
+ "U+" + source_line.charCodeAt(at).toString(16)
+ );
+ }
+ if (!option.white && source_line.slice(-1) === " ") {
+ warn_at(
+ "unexpected_trailing_space",
+ line,
+ source_line.length - 1
+ );
+ }
+ }
+ return source_line;
+ }
+
+// Most tokens, including the identifiers, operators, and punctuators, can be
+// found with a regular expression. Regular expressions cannot correctly match
+// regular expression literals, so we will match those the hard way. String
+// literals and number literals can be matched by regular expressions, but they
+// don't provide good warnings. The functions snip, next_char, prev_char,
+// some_digits, and escape help in the parsing of literals.
+
+ function snip() {
+
+// Remove the last character from snippet.
+
+ snippet = snippet.slice(0, -1);
+ }
+
+ function next_char(match) {
+
+// Get the next character from the source line. Remove it from the source_line,
+// and append it to the snippet. Optionally check that the previous character
+// matched an expected value.
+
+ if (match !== undefined && char !== match) {
+ return stop_at(
+ (
+ char === ""
+ ? "expected_a"
+ : "expected_a_b"
+ ),
+ line,
+ column - 1,
+ match,
+ char
+ );
+ }
+ if (source_line) {
+ char = source_line[0];
+ source_line = source_line.slice(1);
+ snippet += char;
+ } else {
+ char = "";
+ snippet += " ";
+ }
+ column += 1;
+ return char;
+ }
+
+ function back_char() {
+
+// Back up one character by moving a character from the end of the snippet to
+// the front of the source_line.
+
+ if (snippet) {
+ char = snippet.slice(-1);
+ source_line = char + source_line;
+ column -= 1;
+ snip();
+ } else {
+ char = "";
+ }
+ return char;
+ }
+
+ function some_digits(rx, quiet) {
+ const result = source_line.match(rx);
+ if (result) {
+ char = result[1];
+ column += char.length;
+ source_line = result[2];
+ snippet += char;
+ } else {
+ char = "";
+ if (!quiet) {
+ warn_at(
+ "expected_digits_after_a",
+ line,
+ column,
+ snippet
+ );
+ }
+ }
+ return char.length;
+ }
+
+ function escape(extra) {
+ next_char("\\");
+ if (escapeable[char] === true) {
+ return next_char();
+ }
+ if (char === "") {
+ return stop_at("unclosed_string", line, column);
+ }
+ if (char === "u") {
+ if (next_char("u") === "{") {
+ if (json_mode) {
+ warn_at("unexpected_a", line, column - 1, char);
+ }
+ if (some_digits(rx_hexs) > 5) {
+ warn_at("too_many_digits", line, column - 1);
+ }
+ if (next_char() !== "}") {
+ stop_at("expected_a_before_b", line, column, "}", char);
+ }
+ return next_char();
+ }
+ back_char();
+ if (some_digits(rx_hexs, true) < 4) {
+ warn_at("expected_four_digits", line, column - 1);
+ }
+ return;
+ }
+ if (extra && extra.indexOf(char) >= 0) {
+ return next_char();
+ }
+ warn_at("unexpected_a_before_b", line, column - 2, "\\", char);
+ }
+
+ function make(id, value, identifier) {
+
+// Make the token object and append it to the tokens list.
+
+ const the_token = {
+ from,
+ id,
+ identifier: Boolean(identifier),
+ line,
+ nr,
+ thru: column
+ };
+ tokens[nr] = the_token;
+ nr += 1;
+
+// Directives must appear before the first statement.
+
+ if (id !== "(comment)" && id !== ";") {
+ directive_mode = false;
+ }
+
+// If the token is to have a value, give it one.
+
+ if (value !== undefined) {
+ the_token.value = value;
+ }
+
+// If this token is an identifier that touches a preceding number, or
+// a "/", comment, or regular expression literal that touches a preceding
+// comment or regular expression literal, then give a missing space warning.
+// This warning is not suppressed by option.white.
+
+ if (
+ previous.line === line
+ && previous.thru === from
+ && (id === "(comment)" || id === "(regexp)" || id === "/")
+ && (previous.id === "(comment)" || previous.id === "(regexp)")
+ ) {
+ warn(
+ "expected_space_a_b",
+ the_token,
+ artifact(previous),
+ artifact(the_token)
+ );
+ }
+ if (previous.id === "." && id === "(number)") {
+ warn("expected_a_before_b", previous, "0", ".");
+ }
+ if (prior.id === "." && the_token.identifier) {
+ the_token.dot = true;
+ }
+
+// The previous token is used to detect adjacency problems.
+
+ previous = the_token;
+
+// The prior token is a previous token that was not a comment. The prior token
+// is used to disambiguate "/", which can mean division or regular expression
+// literal.
+
+ if (previous.id !== "(comment)") {
+ prior = previous;
+ }
+ return the_token;
+ }
+
+ function parse_directive(the_comment, body) {
+
+// JSLint recognizes three directives that can be encoded in comments. This
+// function processes one item, and calls itself recursively to process the
+// next one.
+
+ const result = body.match(rx_directive_part);
+ if (result) {
+ let allowed;
+ const name = result[1];
+ const value = result[2];
+ if (the_comment.directive === "jslint") {
+ allowed = allowed_option[name];
+ if (
+ typeof allowed === "boolean"
+ || typeof allowed === "object"
+ ) {
+ if (
+ value === ""
+ || value === "true"
+ || value === undefined
+ ) {
+ option[name] = true;
+ if (Array.isArray(allowed)) {
+ populate(allowed, declared_globals, false);
+ }
+ } else if (value === "false") {
+ option[name] = false;
+ } else {
+ warn("bad_option_a", the_comment, name + ":" + value);
+ }
+ } else {
+ warn("bad_option_a", the_comment, name);
+ }
+ } else if (the_comment.directive === "property") {
+ if (tenure === undefined) {
+ tenure = empty();
+ }
+ tenure[name] = true;
+ } else if (the_comment.directive === "global") {
+ if (value) {
+ warn("bad_option_a", the_comment, name + ":" + value);
+ }
+ declared_globals[name] = false;
+ module_mode = the_comment;
+ }
+ return parse_directive(the_comment, result[3]);
+ }
+ if (body) {
+ return stop("bad_directive_a", the_comment, body);
+ }
+ }
+
+ function comment(snippet) {
+
+// Make a comment object. Comments are not allowed in JSON text. Comments can
+// include directives and notices of incompletion.
+
+ const the_comment = make("(comment)", snippet);
+ if (Array.isArray(snippet)) {
+ snippet = snippet.join(" ");
+ }
+ if (!option.devel && rx_todo.test(snippet)) {
+ warn("todo_comment", the_comment);
+ }
+ const result = snippet.match(rx_directive);
+ if (result) {
+ if (!directive_mode) {
+ warn_at("misplaced_directive_a", line, from, result[1]);
+ } else {
+ the_comment.directive = result[1];
+ parse_directive(the_comment, result[2]);
+ }
+ directives.push(the_comment);
+ }
+ return the_comment;
+ }
+
+ function regexp() {
+
+// Parse a regular expression literal.
+
+ let multi_mode = false;
+ let result;
+ let value;
+ regexp_seen = true;
+
+ function quantifier() {
+
+// Match an optional quantifier.
+
+ if (char === "?" || char === "*" || char === "+") {
+ next_char();
+ } else if (char === "{") {
+ if (some_digits(rx_digits, true) === 0) {
+ warn_at("expected_a", line, column, "0");
+ }
+ if (next_char() === ",") {
+ some_digits(rx_digits, true);
+ next_char();
+ }
+ next_char("}");
+ } else {
+ return;
+ }
+ if (char === "?") {
+ next_char("?");
+ }
+ }
+
+ function subklass() {
+
+// Match a character in a character class.
+
+ if (char === "\\") {
+ escape("BbDdSsWw-[]^");
+ return true;
+ }
+ if (
+ char === ""
+ || char === "["
+ || char === "]"
+ || char === "/"
+ || char === "^"
+ || char === "-"
+ ) {
+ return false;
+ }
+ if (char === " ") {
+ warn_at("expected_a_b", line, column, "\\u0020", " ");
+ } else if (char === "`" && mega_mode) {
+ warn_at("unexpected_a", line, column, "`");
+ }
+ next_char();
+ return true;
+ }
+
+ function ranges() {
+
+// Match a range of subclasses.
+
+ if (subklass()) {
+ if (char === "-") {
+ next_char("-");
+ if (!subklass()) {
+ return stop_at(
+ "unexpected_a",
+ line,
+ column - 1,
+ "-"
+ );
+ }
+ }
+ return ranges();
+ }
+ }
+
+ function klass() {
+
+// Match a class.
+
+ next_char("[");
+ if (char === "^") {
+ next_char("^");
+ }
+ (function classy() {
+ ranges();
+ if (char !== "]" && char !== "") {
+ warn_at(
+ "expected_a_before_b",
+ line,
+ column,
+ "\\",
+ char
+ );
+ next_char();
+ return classy();
+ }
+ }());
+ next_char("]");
+ }
+
+ function choice() {
+
+ function group() {
+
+// Match a group that starts with left paren.
+
+ next_char("(");
+ if (char === "?") {
+ next_char("?");
+ if (char === "=" || char === "!") {
+ next_char();
+ } else {
+ next_char(":");
+ }
+ } else if (char === ":") {
+ warn_at("expected_a_before_b", line, column, "?", ":");
+ }
+ choice();
+ next_char(")");
+ }
+
+ function factor() {
+ if (
+ char === ""
+ || char === "/"
+ || char === "]"
+ || char === ")"
+ ) {
+ return false;
+ }
+ if (char === "(") {
+ group();
+ return true;
+ }
+ if (char === "[") {
+ klass();
+ return true;
+ }
+ if (char === "\\") {
+ escape("BbDdSsWw^${}[]():=!.-|*+?");
+ return true;
+ }
+ if (
+ char === "?"
+ || char === "+"
+ || char === "*"
+ || char === "}"
+ || char === "{"
+ ) {
+ warn_at(
+ "expected_a_before_b",
+ line,
+ column - 1,
+ "\\",
+ char
+ );
+ } else if (char === "`") {
+ if (mega_mode) {
+ warn_at("unexpected_a", line, column - 1, "`");
+ }
+ } else if (char === " ") {
+ warn_at(
+ "expected_a_b",
+ line,
+ column - 1,
+ "\\s",
+ " "
+ );
+ } else if (char === "$") {
+ if (source_line[0] !== "/") {
+ multi_mode = true;
+ }
+ } else if (char === "^") {
+ if (snippet !== "^") {
+ multi_mode = true;
+ }
+ }
+ next_char();
+ return true;
+ }
+
+ function sequence(follow) {
+ if (factor()) {
+ quantifier();
+ return sequence(true);
+ }
+ if (!follow) {
+ warn_at("expected_regexp_factor_a", line, column, char);
+ }
+ }
+
+// Match a choice (a sequence that can be followed by | and another choice).
+
+ sequence();
+ if (char === "|") {
+ next_char("|");
+ return choice();
+ }
+ }
+
+// Scan the regexp literal. Give a warning if the first character is = because
+// /= looks like a division assignment operator.
+
+ snippet = "";
+ next_char();
+ if (char === "=") {
+ warn_at("expected_a_before_b", line, column, "\\", "=");
+ }
+ choice();
+
+// Make sure there is a closing slash.
+
+ snip();
+ value = snippet;
+ next_char("/");
+
+// Process dangling flag letters.
+
+ const allowed = {
+ g: true,
+ i: true,
+ m: true,
+ u: true,
+ y: true
+ };
+ const flag = empty();
+ (function make_flag() {
+ if (is_letter(char)) {
+ if (allowed[char] !== true) {
+ warn_at("unexpected_a", line, column, char);
+ }
+ allowed[char] = false;
+ flag[char] = true;
+ next_char();
+ return make_flag();
+ }
+ }());
+ back_char();
+ if (char === "/" || char === "*") {
+ return stop_at("unexpected_a", line, from, char);
+ }
+ result = make("(regexp)", char);
+ result.flag = flag;
+ result.value = value;
+ if (multi_mode && !flag.m) {
+ warn_at("missing_m", line, column);
+ }
+ return result;
+ }
+
+ function string(quote) {
+
+// Make a string token.
+
+ let the_token;
+ snippet = "";
+ next_char();
+
+ return (function next() {
+ if (char === quote) {
+ snip();
+ the_token = make("(string)", snippet);
+ the_token.quote = quote;
+ return the_token;
+ }
+ if (char === "") {
+ return stop_at("unclosed_string", line, column);
+ }
+ if (char === "\\") {
+ escape(quote);
+ } else if (char === "`") {
+ if (mega_mode) {
+ warn_at("unexpected_a", line, column, "`");
+ }
+ next_char("`");
+ } else {
+ next_char();
+ }
+ return next();
+ }());
+ }
+
+ function frack() {
+ if (char === ".") {
+ some_digits(rx_digits);
+ next_char();
+ }
+ if (char === "E" || char === "e") {
+ next_char();
+ if (char !== "+" && char !== "-") {
+ back_char();
+ }
+ some_digits(rx_digits);
+ next_char();
+ }
+ }
+
+ function number() {
+ if (snippet === "0") {
+ next_char();
+ if (char === ".") {
+ frack();
+ } else if (char === "b") {
+ some_digits(rx_bits);
+ next_char();
+ } else if (char === "o") {
+ some_digits(rx_octals);
+ next_char();
+ } else if (char === "x") {
+ some_digits(rx_hexs);
+ next_char();
+ }
+ } else {
+ next_char();
+ frack();
+ }
+
+// If the next character after a number is a digit or letter, then something
+// unexpected is going on.
+
+ if (
+ (char >= "0" && char <= "9")
+ || (char >= "a" && char <= "z")
+ || (char >= "A" && char <= "Z")
+ ) {
+ return stop_at(
+ "unexpected_a_after_b",
+ line,
+ column - 1,
+ snippet.slice(-1),
+ snippet.slice(0, -1)
+ );
+ }
+ back_char();
+ return make("(number)", snippet);
+ }
+
+ function lex() {
+ let array;
+ let i = 0;
+ let j = 0;
+ let last;
+ let result;
+ let the_token;
+ if (!source_line) {
+ source_line = next_line();
+ from = 0;
+ return (
+ source_line === undefined
+ ? (
+ mega_mode
+ ? stop_at("unclosed_mega", mega_line, mega_from)
+ : make("(end)")
+ )
+ : lex()
+ );
+ }
+ from = column;
+ result = source_line.match(rx_token);
+
+// result[1] token
+// result[2] whitespace
+// result[3] identifier
+// result[4] number
+// result[5] rest
+
+ if (!result) {
+ return stop_at(
+ "unexpected_char_a",
+ line,
+ column,
+ source_line[0]
+ );
+ }
+
+ snippet = result[1];
+ column += snippet.length;
+ source_line = result[5];
+
+// Whitespace was matched. Call lex again to get more.
+
+ if (result[2]) {
+ return lex();
+ }
+
+// The token is an identifier.
+
+ if (result[3]) {
+ return make(snippet, undefined, true);
+ }
+
+// The token is a number.
+
+ if (result[4]) {
+ return number(snippet);
+ }
+
+// The token is a string.
+
+ if (snippet === "\"") {
+ return string(snippet);
+ }
+ if (snippet === "'") {
+ if (!option.single) {
+ warn_at("use_double", line, column);
+ }
+ return string(snippet);
+ }
+
+// The token is a megastring. We don't allow any kind of mega nesting.
+
+ if (snippet === "`") {
+ if (mega_mode) {
+ return stop_at("expected_a_b", line, column, "}", "`");
+ }
+ snippet = "";
+ mega_from = from;
+ mega_line = line;
+ mega_mode = true;
+
+// Parsing a mega literal is tricky. First make a ` token.
+
+ make("`");
+ from += 1;
+
+// Then loop, building up a string, possibly from many lines, until seeing
+// the end of file, a closing `, or a ${ indicting an expression within the
+// string.
+
+ (function part() {
+ const at = source_line.search(rx_mega);
+
+// If neither ` nor ${ is seen, then the whole line joins the snippet.
+
+ if (at < 0) {
+ snippet += source_line + "\n";
+ return (
+ next_line() === undefined
+ ? stop_at("unclosed_mega", mega_line, mega_from)
+ : part()
+ );
+ }
+
+// if either ` or ${ was found, then the preceding joins the snippet to become
+// a string token.
+
+ snippet += source_line.slice(0, at);
+ column += at;
+ source_line = source_line.slice(at);
+ if (source_line[0] === "\\") {
+ stop_at("escape_mega", line, at);
+ }
+ make("(string)", snippet).quote = "`";
+ snippet = "";
+
+// If ${, then make tokens that will become part of an expression until
+// a } token is made.
+
+ if (source_line[0] === "$") {
+ column += 2;
+ make("${");
+ source_line = source_line.slice(2);
+ (function expr() {
+ const id = lex().id;
+ if (id === "{") {
+ return stop_at(
+ "expected_a_b",
+ line,
+ column,
+ "}",
+ "{"
+ );
+ }
+ if (id !== "}") {
+ return expr();
+ }
+ }());
+ return part();
+ }
+ }());
+ source_line = source_line.slice(1);
+ column += 1;
+ mega_mode = false;
+ return make("`");
+ }
+
+// The token is a // comment.
+
+ if (snippet === "//") {
+ snippet = source_line;
+ source_line = "";
+ the_token = comment(snippet);
+ if (mega_mode) {
+ warn("unexpected_comment", the_token, "`");
+ }
+ return the_token;
+ }
+
+// The token is a /* comment.
+
+ if (snippet === "/*") {
+ array = [];
+ if (source_line[0] === "/") {
+ warn_at("unexpected_a", line, column + i, "/");
+ }
+ (function next() {
+ if (source_line > "") {
+ i = source_line.search(rx_star_slash);
+ if (i >= 0) {
+ return;
+ }
+ j = source_line.search(rx_slash_star);
+ if (j >= 0) {
+ warn_at("nested_comment", line, column + j);
+ }
+ }
+ array.push(source_line);
+ source_line = next_line();
+ if (source_line === undefined) {
+ return stop_at("unclosed_comment", line, column);
+ }
+ return next();
+ }());
+ snippet = source_line.slice(0, i);
+ j = snippet.search(rx_slash_star_or_slash);
+ if (j >= 0) {
+ warn_at("nested_comment", line, column + j);
+ }
+ array.push(snippet);
+ column += i + 2;
+ source_line = source_line.slice(i + 2);
+ return comment(array);
+ }
+
+// The token is a slash.
+
+ if (snippet === "/") {
+
+// The / can be a division operator or the beginning of a regular expression
+// literal. It is not possible to know which without doing a complete parse.
+// We want to complete the tokenization before we begin to parse, so we will
+// estimate. This estimator can fail in some cases. For example, it cannot
+// know if "}" is ending a block or ending an object literal, so it can
+// behave incorrectly in that case; it is not meaningful to divide an
+// object, so it is likely that we can get away with it. We avoided the worst
+// cases by eliminating automatic semicolon insertion.
+
+ if (prior.identifier) {
+ if (!prior.dot) {
+ if (prior.id === "return") {
+ return regexp();
+ }
+ if (
+ prior.id === "(begin)"
+ || prior.id === "case"
+ || prior.id === "delete"
+ || prior.id === "in"
+ || prior.id === "instanceof"
+ || prior.id === "new"
+ || prior.id === "typeof"
+ || prior.id === "void"
+ || prior.id === "yield"
+ ) {
+ the_token = regexp();
+ return stop("unexpected_a", the_token);
+ }
+ }
+ } else {
+ last = prior.id[prior.id.length - 1];
+ if ("(,=:?[".indexOf(last) >= 0) {
+ return regexp();
+ }
+ if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
+ the_token = regexp();
+ warn("wrap_regexp", the_token);
+ return the_token;
+ }
+ }
+ if (source_line[0] === "/") {
+ column += 1;
+ source_line = source_line.slice(1);
+ snippet = "/=";
+ warn_at("unexpected_a", line, column, "/=");
+ }
+ }
+ return make(snippet);
+ }
+
+ first = lex();
+ json_mode = first.id === "{" || first.id === "[";
+
+// This is the only loop in JSLint. It will turn into a recursive call to lex
+// when ES6 has been finished and widely deployed and adopted.
+
+ while (true) {
+ if (lex().id === "(end)") {
+ break;
+ }
+ }
+}
+
+// Parsing:
+
+// Parsing weaves the tokens into an abstract syntax tree. During that process,
+// a token may be given any of these properties:
+
+// arity string
+// label identifier
+// name identifier
+// expression expressions
+// block statements
+// else statements (else, default, catch)
+
+// Specialized tokens may have additional properties.
+
+function survey(name) {
+ let id = name.id;
+
+// Tally the property name. If it is a string, only tally strings that conform
+// to the identifier rules.
+
+ if (id === "(string)") {
+ id = name.value;
+ if (!rx_identifier.test(id)) {
+ return id;
+ }
+ } else if (id === "`") {
+ if (name.value.length === 1) {
+ id = name.value[0].value;
+ if (!rx_identifier.test(id)) {
+ return id;
+ }
+ }
+ } else if (!name.identifier) {
+ return stop("expected_identifier_a", name);
+ }
+
+// If we have seen this name before, increment its count.
+
+ if (typeof property[id] === "number") {
+ property[id] += 1;
+
+// If this is the first time seeing this property name, and if there is a
+// tenure list, then it must be on the list. Otherwise, it must conform to
+// the rules for good property names.
+
+ } else {
+ if (tenure !== undefined) {
+ if (tenure[id] !== true) {
+ warn("unregistered_property_a", name);
+ }
+ } else {
+ if (name.identifier && rx_bad_property.test(id)) {
+ warn("bad_property_a", name);
+ }
+ }
+ property[id] = 1;
+ }
+ return id;
+}
+
+function dispense() {
+
+// Deliver the next token, skipping the comments.
+
+ const cadet = tokens[token_nr];
+ token_nr += 1;
+ if (cadet.id === "(comment)") {
+ if (json_mode) {
+ warn("unexpected_a", cadet);
+ }
+ return dispense();
+ } else {
+ return cadet;
+ }
+}
+
+function lookahead() {
+
+// Look ahead one token without advancing.
+
+ const old_token_nr = token_nr;
+ const cadet = dispense(true);
+ token_nr = old_token_nr;
+ return cadet;
+}
+
+function advance(id, match) {
+
+// Produce the next token.
+
+// Attempt to give helpful names to anonymous functions.
+
+ if (token.identifier && token.id !== "function") {
+ anon = token.id;
+ } else if (token.id === "(string)" && rx_identifier.test(token.value)) {
+ anon = token.value;
+ }
+
+// Attempt to match next_token with an expected id.
+
+ if (id !== undefined && next_token.id !== id) {
+ return (
+ match === undefined
+ ? stop("expected_a_b", next_token, id, artifact())
+ : stop(
+ "expected_a_b_from_c_d",
+ next_token,
+ id,
+ artifact(match),
+ artifact_line(match),
+ artifact(next_token)
+ )
+ );
+ }
+
+// Promote the tokens, skipping comments.
+
+ token = next_token;
+ next_token = dispense();
+ if (next_token.id === "(end)") {
+ token_nr -= 1;
+ }
+}
+
+// Parsing of JSON is simple:
+
+function json_value() {
+ let negative;
+ if (next_token.id === "{") {
+ return (function json_object() {
+ const brace = next_token;
+ const object = empty();
+ const properties = [];
+ brace.expression = properties;
+ advance("{");
+ if (next_token.id !== "}") {
+ (function next() {
+ let name;
+ let value;
+ if (next_token.quote !== "\"") {
+ warn(
+ "unexpected_a",
+ next_token,
+ next_token.quote
+ );
+ }
+ name = next_token;
+ advance("(string)");
+ if (object[token.value] !== undefined) {
+ warn("duplicate_a", token);
+ } else if (token.value === "__proto__") {
+ warn("bad_property_a", token);
+ } else {
+ object[token.value] = token;
+ }
+ advance(":");
+ value = json_value();
+ value.label = name;
+ properties.push(value);
+ if (next_token.id === ",") {
+ advance(",");
+ return next();
+ }
+ }());
+ }
+ advance("}", brace);
+ return brace;
+ }());
+ }
+ if (next_token.id === "[") {
+ return (function json_array() {
+ const bracket = next_token;
+ const elements = [];
+ bracket.expression = elements;
+ advance("[");
+ if (next_token.id !== "]") {
+ (function next() {
+ elements.push(json_value());
+ if (next_token.id === ",") {
+ advance(",");
+ return next();
+ }
+ }());
+ }
+ advance("]", bracket);
+ return bracket;
+ }());
+ }
+ if (
+ next_token.id === "true"
+ || next_token.id === "false"
+ || next_token.id === "null"
+ ) {
+ advance();
+ return token;
+ }
+ if (next_token.id === "(number)") {
+ if (!rx_JSON_number.test(next_token.value)) {
+ warn("unexpected_a");
+ }
+ advance();
+ return token;
+ }
+ if (next_token.id === "(string)") {
+ if (next_token.quote !== "\"") {
+ warn("unexpected_a", next_token, next_token.quote);
+ }
+ advance();
+ return token;
+ }
+ if (next_token.id === "-") {
+ negative = next_token;
+ negative.arity = "unary";
+ advance("-");
+ advance("(number)");
+ negative.expression = token;
+ return negative;
+ }
+ stop("unexpected_a");
+}
+
+// Now we parse JavaScript.
+
+function enroll(name, role, readonly) {
+
+// Enroll a name into the current function context. The role can be exception,
+// function, label, parameter, or variable. We look for variable redefinition
+// because it causes confusion.
+
+ const id = name.id;
+
+// Reserved words may not be enrolled.
+
+ if (syntax[id] !== undefined && id !== "ignore") {
+ warn("reserved_a", name);
+ } else {
+
+// Has the name been enrolled in this context?
+
+ let earlier = functionage.context[id];
+ if (earlier) {
+ warn(
+ "redefinition_a_b",
+ name,
+ name.id,
+ earlier.line + fudge
+ );
+
+// Has the name been enrolled in an outer context?
+
+ } else {
+ stack.forEach(function (value) {
+ const item = value.context[id];
+ if (item !== undefined) {
+ earlier = item;
+ }
+ });
+ if (earlier) {
+ if (id === "ignore") {
+ if (earlier.role === "variable") {
+ warn("unexpected_a", name);
+ }
+ } else {
+ if (
+ (
+ role !== "exception"
+ || earlier.role !== "exception"
+ )
+ && role !== "parameter"
+ && role !== "function"
+ ) {
+ warn(
+ "redefinition_a_b",
+ name,
+ name.id,
+ earlier.line + fudge
+ );
+ }
+ }
+ }
+
+// Enroll it.
+
+ functionage.context[id] = name;
+ name.dead = true;
+ name.parent = functionage;
+ name.init = false;
+ name.role = role;
+ name.used = 0;
+ name.writable = !readonly;
+ }
+ }
+}
+
+function expression(rbp, initial) {
+
+// This is the heart of the Pratt parser. I retained Pratt's nomenclature.
+// They are elements of the parsing method called Top Down Operator Precedence.
+
+// nud Null denotation
+// led Left denotation
+// lbp Left binding power
+// rbp Right binding power
+
+// It processes a nud (variable, constant, prefix operator). It will then
+// process leds (infix operators) until the bind powers cause it to stop. It
+// returns the expression's parse tree.
+
+ let left;
+ let the_symbol;
+
+// Statements will have already advanced, so advance now only if the token is
+// not the first of a statement,
+
+ if (!initial) {
+ advance();
+ }
+ the_symbol = syntax[token.id];
+ if (the_symbol !== undefined && the_symbol.nud !== undefined) {
+ left = the_symbol.nud();
+ } else if (token.identifier) {
+ left = token;
+ left.arity = "variable";
+ } else {
+ return stop("unexpected_a", token);
+ }
+ (function right() {
+ the_symbol = syntax[next_token.id];
+ if (
+ the_symbol !== undefined
+ && the_symbol.led !== undefined
+ && rbp < the_symbol.lbp
+ ) {
+ advance();
+ left = the_symbol.led(left);
+ return right();
+ }
+ }());
+ return left;
+}
+
+function condition() {
+
+// Parse the condition part of a do, if, while.
+
+ const the_paren = next_token;
+ let the_value;
+ the_paren.free = true;
+ advance("(");
+ the_value = expression(0);
+ advance(")");
+ if (the_value.wrapped === true) {
+ warn("unexpected_a", the_paren);
+ }
+ if (anticondition[the_value.id] === true) {
+ warn("unexpected_a", the_value);
+ }
+ return the_value;
+}
+
+function is_weird(thing) {
+ return (
+ thing.id === "(regexp)"
+ || thing.id === "{"
+ || thing.id === "=>"
+ || thing.id === "function"
+ || (thing.id === "[" && thing.arity === "unary")
+ );
+}
+
+function are_similar(a, b) {
+ if (a === b) {
+ return true;
+ }
+ if (Array.isArray(a)) {
+ return (
+ Array.isArray(b)
+ && a.length === b.length
+ && a.every(function (value, index) {
+ return are_similar(value, b[index]);
+ })
+ );
+ }
+ if (Array.isArray(b)) {
+ return false;
+ }
+ if (a.id === "(number)" && b.id === "(number)") {
+ return a.value === b.value;
+ }
+ let a_string;
+ let b_string;
+ if (a.id === "(string)") {
+ a_string = a.value;
+ } else if (a.id === "`" && a.constant) {
+ a_string = a.value[0];
+ }
+ if (b.id === "(string)") {
+ b_string = b.value;
+ } else if (b.id === "`" && b.constant) {
+ b_string = b.value[0];
+ }
+ if (typeof a_string === "string") {
+ return a_string === b_string;
+ }
+ if (is_weird(a) || is_weird(b)) {
+ return false;
+ }
+ if (a.arity === b.arity && a.id === b.id) {
+ if (a.id === ".") {
+ return (
+ are_similar(a.expression, b.expression)
+ && are_similar(a.name, b.name)
+ );
+ }
+ if (a.arity === "unary") {
+ return are_similar(a.expression, b.expression);
+ }
+ if (a.arity === "binary") {
+ return (
+ a.id !== "("
+ && are_similar(a.expression[0], b.expression[0])
+ && are_similar(a.expression[1], b.expression[1])
+ );
+ }
+ if (a.arity === "ternary") {
+ return (
+ are_similar(a.expression[0], b.expression[0])
+ && are_similar(a.expression[1], b.expression[1])
+ && are_similar(a.expression[2], b.expression[2])
+ );
+ }
+ if (a.arity === "function" && a.arity === "regexp") {
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+function semicolon() {
+
+// Try to match a semicolon.
+
+ if (next_token.id === ";") {
+ advance(";");
+ } else {
+ warn_at(
+ "expected_a_b",
+ token.line,
+ token.thru,
+ ";",
+ artifact(next_token)
+ );
+ }
+ anon = "anonymous";
+}
+
+function statement() {
+
+// Parse a statement. Any statement may have a label, but only four statements
+// have use for one. A statement can be one of the standard statements, or
+// an assignment expression, or an invocation expression.
+
+ let first;
+ let the_label;
+ let the_statement;
+ let the_symbol;
+ advance();
+ if (token.identifier && next_token.id === ":") {
+ the_label = token;
+ if (the_label.id === "ignore") {
+ warn("unexpected_a", the_label);
+ }
+ advance(":");
+ if (
+ next_token.id === "do"
+ || next_token.id === "for"
+ || next_token.id === "switch"
+ || next_token.id === "while"
+ ) {
+ enroll(the_label, "label", true);
+ the_label.init = true;
+ the_label.dead = false;
+ the_statement = statement();
+ the_statement.label = the_label;
+ the_statement.statement = true;
+ return the_statement;
+ }
+ advance();
+ warn("unexpected_label_a", the_label);
+ }
+
+// Parse the statement.
+
+ first = token;
+ first.statement = true;
+ the_symbol = syntax[first.id];
+ if (the_symbol !== undefined && the_symbol.fud !== undefined) {
+ the_symbol.disrupt = false;
+ the_symbol.statement = true;
+ the_statement = the_symbol.fud();
+ } else {
+
+// It is an expression statement.
+
+ the_statement = expression(0, true);
+ if (the_statement.wrapped && the_statement.id !== "(") {
+ warn("unexpected_a", first);
+ }
+ semicolon();
+ }
+ if (the_label !== undefined) {
+ the_label.dead = true;
+ }
+ return the_statement;
+}
+
+function statements() {
+
+// Parse a list of statements. Give a warning if an unreachable statement
+// follows a disruptive statement.
+
+ const array = [];
+ (function next(disrupt) {
+ if (
+ next_token.id !== "}"
+ && next_token.id !== "case"
+ && next_token.id !== "default"
+ && next_token.id !== "else"
+ && next_token.id !== "(end)"
+ ) {
+ let a_statement = statement();
+ array.push(a_statement);
+ if (disrupt) {
+ warn("unreachable_a", a_statement);
+ }
+ return next(a_statement.disrupt);
+ }
+ }(false));
+ return array;
+}
+
+function not_top_level(thing) {
+
+// Some features should not be at the outermost level.
+
+ if (functionage === global) {
+ warn("unexpected_at_top_level_a", thing);
+ }
+}
+
+function top_level_only(the_thing) {
+
+// Some features must be at the most outermost level.
+
+ if (blockage !== global) {
+ warn("misplaced_a", the_thing);
+ }
+}
+
+function block(special) {
+
+// Parse a block, a sequence of statements wrapped in braces.
+// special "body" The block is a function body.
+// "ignore" No warning on an empty block.
+// "naked" No advance.
+// undefined An ordinary block.
+
+ let stmts;
+ let the_block;
+ if (special !== "naked") {
+ advance("{");
+ }
+ the_block = token;
+ the_block.arity = "statement";
+ the_block.body = special === "body";
+
+// Top level function bodies may include the "use strict" pragma.
+
+ if (
+ special === "body"
+ && stack.length === 1
+ && next_token.value === "use strict"
+ ) {
+ next_token.statement = true;
+ advance("(string)");
+ advance(";");
+ }
+ stmts = statements();
+ the_block.block = stmts;
+ if (stmts.length === 0) {
+ if (!option.devel && special !== "ignore") {
+ warn("empty_block", the_block);
+ }
+ the_block.disrupt = false;
+ } else {
+ the_block.disrupt = stmts[stmts.length - 1].disrupt;
+ }
+ advance("}");
+ return the_block;
+}
+
+function mutation_check(the_thing) {
+
+// The only expressions that may be assigned to are
+// e.b
+// e[b]
+// v
+// [destructure]
+// {destructure}
+
+ if (
+ the_thing.arity !== "variable"
+ && the_thing.id !== "."
+ && the_thing.id !== "["
+ && the_thing.id !== "{"
+ ) {
+ warn("bad_assignment_a", the_thing);
+ return false;
+ }
+ return true;
+}
+
+function left_check(left, right) {
+
+// Warn if the left is not one of these:
+// e.b
+// e[b]
+// e()
+// ?:
+// identifier
+
+ const id = left.id;
+ if (
+ !left.identifier
+ && (
+ left.arity !== "ternary"
+ || (
+ !left_check(left.expression[1])
+ && !left_check(left.expression[2])
+ )
+ )
+ && (
+ left.arity !== "binary"
+ || (id !== "." && id !== "(" && id !== "[")
+ )
+ ) {
+ warn("unexpected_a", right);
+ return false;
+ }
+ return true;
+}
+
+// These functions are used to specify the grammar of our language:
+
+function symbol(id, bp) {
+
+// Make a symbol if it does not already exist in the language's syntax.
+
+ let the_symbol = syntax[id];
+ if (the_symbol === undefined) {
+ the_symbol = empty();
+ the_symbol.id = id;
+ the_symbol.lbp = bp || 0;
+ syntax[id] = the_symbol;
+ }
+ return the_symbol;
+}
+
+function assignment(id) {
+
+// Make an assignment operator. The one true assignment is different because
+// its left side, when it is a variable, is not treated as an expression.
+// That case is special because that is when a variable gets initialized. The
+// other assignment operators can modify, but they cannot initialize.
+
+ const the_symbol = symbol(id, 20);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ let right;
+ the_token.arity = "assignment";
+ right = expression(20 - 1);
+ if (id === "=" && left.arity === "variable") {
+ the_token.names = left;
+ the_token.expression = right;
+ } else {
+ the_token.expression = [left, right];
+ }
+ if (
+ right.arity === "assignment"
+ || right.arity === "pre"
+ || right.arity === "post"
+ ) {
+ warn("unexpected_a", right);
+ }
+ mutation_check(left);
+ return the_token;
+ };
+ return the_symbol;
+}
+
+function constant(id, type, value) {
+
+// Make a constant symbol.
+
+ const the_symbol = symbol(id);
+ the_symbol.constant = true;
+ the_symbol.nud = (
+ typeof value === "function"
+ ? value
+ : function () {
+ token.constant = true;
+ if (value !== undefined) {
+ token.value = value;
+ }
+ return token;
+ }
+ );
+ the_symbol.type = type;
+ the_symbol.value = value;
+ return the_symbol;
+}
+
+function infix(id, bp, f) {
+
+// Make an infix operator.
+
+ const the_symbol = symbol(id, bp);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ the_token.arity = "binary";
+ if (f !== undefined) {
+ return f(left);
+ }
+ the_token.expression = [left, expression(bp)];
+ return the_token;
+ };
+ return the_symbol;
+}
+
+function infixr(id, bp) {
+
+// Make a right associative infix operator.
+
+ const the_symbol = symbol(id, bp);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ the_token.arity = "binary";
+ the_token.expression = [left, expression(bp - 1)];
+ return the_token;
+ };
+ return the_symbol;
+}
+
+function post(id) {
+
+// Make one of the post operators.
+
+ const the_symbol = symbol(id, 150);
+ the_symbol.led = function (left) {
+ token.expression = left;
+ token.arity = "post";
+ mutation_check(token.expression);
+ return token;
+ };
+ return the_symbol;
+}
+
+function pre(id) {
+
+// Make one of the pre operators.
+
+ const the_symbol = symbol(id);
+ the_symbol.nud = function () {
+ const the_token = token;
+ the_token.arity = "pre";
+ the_token.expression = expression(150);
+ mutation_check(the_token.expression);
+ return the_token;
+ };
+ return the_symbol;
+}
+
+function prefix(id, f) {
+
+// Make a prefix operator.
+
+ const the_symbol = symbol(id);
+ the_symbol.nud = function () {
+ const the_token = token;
+ the_token.arity = "unary";
+ if (typeof f === "function") {
+ return f();
+ }
+ the_token.expression = expression(150);
+ return the_token;
+ };
+ return the_symbol;
+}
+
+function stmt(id, f) {
+
+// Make a statement.
+
+ const the_symbol = symbol(id);
+ the_symbol.fud = function () {
+ token.arity = "statement";
+ return f();
+ };
+ return the_symbol;
+}
+
+function ternary(id1, id2) {
+
+// Make a ternary operator.
+
+ const the_symbol = symbol(id1, 30);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ const second = expression(20);
+ advance(id2);
+ token.arity = "ternary";
+ the_token.arity = "ternary";
+ the_token.expression = [left, second, expression(10)];
+ if (next_token.id !== ")") {
+ warn("use_open", the_token);
+ }
+ return the_token;
+ };
+ return the_symbol;
+}
+
+// Begin defining the language.
+
+syntax = empty();
+
+symbol("}");
+symbol(")");
+symbol("]");
+symbol(",");
+symbol(";");
+symbol(":");
+symbol("*/");
+symbol("await");
+symbol("case");
+symbol("catch");
+symbol("class");
+symbol("default");
+symbol("else");
+symbol("enum");
+symbol("finally");
+symbol("implements");
+symbol("interface");
+symbol("package");
+symbol("private");
+symbol("protected");
+symbol("public");
+symbol("static");
+symbol("super");
+symbol("void");
+symbol("yield");
+
+constant("(number)", "number");
+constant("(regexp)", "regexp");
+constant("(string)", "string");
+constant("arguments", "object", function () {
+ warn("unexpected_a", token);
+ return token;
+});
+constant("eval", "function", function () {
+ if (!option.eval) {
+ warn("unexpected_a", token);
+ } else if (next_token.id !== "(") {
+ warn("expected_a_before_b", next_token, "(", artifact());
+ }
+ return token;
+});
+constant("false", "boolean", false);
+constant("Function", "function", function () {
+ if (!option.eval) {
+ warn("unexpected_a", token);
+ } else if (next_token.id !== "(") {
+ warn("expected_a_before_b", next_token, "(", artifact());
+ }
+ return token;
+});
+constant("ignore", "undefined", function () {
+ warn("unexpected_a", token);
+ return token;
+});
+constant("Infinity", "number", Infinity);
+constant("isFinite", "function", function () {
+ warn("expected_a_b", token, "Number.isFinite", "isFinite");
+ return token;
+});
+constant("isNaN", "function", function () {
+ warn("number_isNaN", token);
+ return token;
+});
+constant("NaN", "number", NaN);
+constant("null", "null", null);
+constant("this", "object", function () {
+ if (!option.this) {
+ warn("unexpected_a", token);
+ }
+ return token;
+});
+constant("true", "boolean", true);
+constant("undefined", "undefined");
+
+assignment("=");
+assignment("+=");
+assignment("-=");
+assignment("*=");
+assignment("/=");
+assignment("%=");
+assignment("&=");
+assignment("|=");
+assignment("^=");
+assignment("<<=");
+assignment(">>=");
+assignment(">>>=");
+
+infix("||", 40);
+infix("&&", 50);
+infix("|", 70);
+infix("^", 80);
+infix("&", 90);
+infix("==", 100);
+infix("===", 100);
+infix("!=", 100);
+infix("!==", 100);
+infix("<", 110);
+infix(">", 110);
+infix("<=", 110);
+infix(">=", 110);
+infix("in", 110);
+infix("instanceof", 110);
+infix("<<", 120);
+infix(">>", 120);
+infix(">>>", 120);
+infix("+", 130);
+infix("-", 130);
+infix("*", 140);
+infix("/", 140);
+infix("%", 140);
+infixr("**", 150);
+infix("(", 160, function (left) {
+ const the_paren = token;
+ let the_argument;
+ if (left.id !== "function") {
+ left_check(left, the_paren);
+ }
+ if (functionage.arity === "statement" && left.identifier) {
+ functionage.name.calls[left.id] = left;
+ }
+ the_paren.expression = [left];
+ if (next_token.id !== ")") {
+ (function next() {
+ let ellipsis;
+ if (next_token.id === "...") {
+ ellipsis = true;
+ advance("...");
+ }
+ the_argument = expression(10);
+ if (ellipsis) {
+ the_argument.ellipsis = true;
+ }
+ the_paren.expression.push(the_argument);
+ if (next_token.id === ",") {
+ advance(",");
+ return next();
+ }
+ }());
+ }
+ advance(")", the_paren);
+ if (the_paren.expression.length === 2) {
+ the_paren.free = true;
+ if (the_argument.wrapped === true) {
+ warn("unexpected_a", the_paren);
+ }
+ if (the_argument.id === "(") {
+ the_argument.wrapped = true;
+ }
+ } else {
+ the_paren.free = false;
+ }
+ return the_paren;
+});
+infix(".", 170, function (left) {
+ const the_token = token;
+ const name = next_token;
+ if (
+ (
+ left.id !== "(string)"
+ || (name.id !== "indexOf" && name.id !== "repeat")
+ )
+ && (
+ left.id !== "["
+ || (
+ name.id !== "concat"
+ && name.id !== "forEach"
+ && name.id !== "join"
+ && name.id !== "map"
+ )
+ )
+ && (left.id !== "+" || name.id !== "slice")
+ && (
+ left.id !== "(regexp)"
+ || (name.id !== "exec" && name.id !== "test")
+ )
+ ) {
+ left_check(left, the_token);
+ }
+ if (!name.identifier) {
+ stop("expected_identifier_a");
+ }
+ advance();
+ survey(name);
+
+// The property name is not an expression.
+
+ the_token.name = name;
+ the_token.expression = left;
+ return the_token;
+});
+infix("?.", 170, function (left) {
+ const the_token = token;
+ const name = next_token;
+ if (
+ (
+ left.id !== "(string)"
+ || (name.id !== "indexOf" && name.id !== "repeat")
+ )
+ && (
+ left.id !== "["
+ || (
+ name.id !== "concat"
+ && name.id !== "forEach"
+ && name.id !== "join"
+ && name.id !== "map"
+ )
+ )
+ && (left.id !== "+" || name.id !== "slice")
+ && (
+ left.id !== "(regexp)"
+ || (name.id !== "exec" && name.id !== "test")
+ )
+ ) {
+ left_check(left, the_token);
+ }
+ if (!name.identifier) {
+ stop("expected_identifier_a");
+ }
+ advance();
+ survey(name);
+
+// The property name is not an expression.
+
+ the_token.name = name;
+ the_token.expression = left;
+ return the_token;
+});
+infix("[", 170, function (left) {
+ const the_token = token;
+ const the_subscript = expression(0);
+ if (the_subscript.id === "(string)" || the_subscript.id === "`") {
+ const name = survey(the_subscript);
+ if (rx_identifier.test(name)) {
+ warn("subscript_a", the_subscript, name);
+ }
+ }
+ left_check(left, the_token);
+ the_token.expression = [left, the_subscript];
+ advance("]");
+ return the_token;
+});
+infix("=>", 170, function (left) {
+ return stop("wrap_parameter", left);
+});
+
+function do_tick() {
+ const the_tick = token;
+ the_tick.value = [];
+ the_tick.expression = [];
+ if (next_token.id !== "`") {
+ (function part() {
+ advance("(string)");
+ the_tick.value.push(token);
+ if (next_token.id === "${") {
+ advance("${");
+ the_tick.expression.push(expression(0));
+ advance("}");
+ return part();
+ }
+ }());
+ }
+ advance("`");
+ return the_tick;
+}
+
+infix("`", 160, function (left) {
+ const the_tick = do_tick();
+ left_check(left, the_tick);
+ the_tick.expression = [left].concat(the_tick.expression);
+ return the_tick;
+});
+
+post("++");
+post("--");
+pre("++");
+pre("--");
+
+prefix("+");
+prefix("-");
+prefix("~");
+prefix("!");
+prefix("!!");
+prefix("[", function () {
+ const the_token = token;
+ the_token.expression = [];
+ if (next_token.id !== "]") {
+ (function next() {
+ let element;
+ let ellipsis = false;
+ if (next_token.id === "...") {
+ ellipsis = true;
+ advance("...");
+ }
+ element = expression(10);
+ if (ellipsis) {
+ element.ellipsis = true;
+ }
+ the_token.expression.push(element);
+ if (next_token.id === ",") {
+ advance(",");
+ return next();
+ }
+ }());
+ }
+ advance("]");
+ return the_token;
+});
+prefix("/=", function () {
+ stop("expected_a_b", token, "/\\=", "/=");
+});
+prefix("=>", function () {
+ return stop("expected_a_before_b", token, "()", "=>");
+});
+prefix("new", function () {
+ const the_new = token;
+ const right = expression(160);
+ if (next_token.id !== "(") {
+ warn("expected_a_before_b", next_token, "()", artifact(next_token));
+ }
+ the_new.expression = right;
+ return the_new;
+});
+prefix("typeof");
+prefix("void", function () {
+ const the_void = token;
+ warn("unexpected_a", the_void);
+ the_void.expression = expression(0);
+ return the_void;
+});
+
+function parameter_list() {
+ const list = [];
+ let optional;
+ const signature = ["("];
+ if (next_token.id !== ")" && next_token.id !== "(end)") {
+ (function parameter() {
+ let ellipsis = false;
+ let param;
+ if (next_token.id === "{") {
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ next_token,
+ next_token.id,
+ optional.id
+ );
+ }
+ param = next_token;
+ param.names = [];
+ advance("{");
+ signature.push("{");
+ (function subparameter() {
+ let subparam = next_token;
+ if (!subparam.identifier) {
+ return stop("expected_identifier_a");
+ }
+ survey(subparam);
+ advance();
+ signature.push(subparam.id);
+ if (next_token.id === ":") {
+ advance(":");
+ advance();
+ token.label = subparam;
+ subparam = token;
+ if (!subparam.identifier) {
+ return stop("expected_identifier_a");
+ }
+ }
+ if (next_token.id === "=") {
+ advance("=");
+ subparam.expression = expression();
+ param.open = true;
+ }
+ param.names.push(subparam);
+ if (next_token.id === ",") {
+ advance(",");
+ signature.push(", ");
+ return subparameter();
+ }
+ }());
+ list.push(param);
+ advance("}");
+ signature.push("}");
+ if (next_token.id === ",") {
+ advance(",");
+ signature.push(", ");
+ return parameter();
+ }
+ } else if (next_token.id === "[") {
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ next_token,
+ next_token.id,
+ optional.id
+ );
+ }
+ param = next_token;
+ param.names = [];
+ advance("[");
+ signature.push("[]");
+ (function subparameter() {
+ const subparam = next_token;
+ if (!subparam.identifier) {
+ return stop("expected_identifier_a");
+ }
+ advance();
+ param.names.push(subparam);
+ if (next_token.id === "=") {
+ advance("=");
+ subparam.expression = expression();
+ param.open = true;
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ return subparameter();
+ }
+ }());
+ list.push(param);
+ advance("]");
+ if (next_token.id === ",") {
+ advance(",");
+ signature.push(", ");
+ return parameter();
+ }
+ } else {
+ if (next_token.id === "...") {
+ ellipsis = true;
+ signature.push("...");
+ advance("...");
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ next_token,
+ next_token.id,
+ optional.id
+ );
+ }
+ }
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a");
+ }
+ param = next_token;
+ list.push(param);
+ advance();
+ signature.push(param.id);
+ if (ellipsis) {
+ param.ellipsis = true;
+ } else {
+ if (next_token.id === "=") {
+ optional = param;
+ advance("=");
+ param.expression = expression(0);
+ } else {
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ param,
+ param.id,
+ optional.id
+ );
+ }
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ signature.push(", ");
+ return parameter();
+ }
+ }
+ }
+ }());
+ }
+ advance(")");
+ signature.push(")");
+ return [list, signature.join("")];
+}
+
+function do_function(the_function) {
+ let name;
+ if (the_function === undefined) {
+ the_function = token;
+
+// A function statement must have a name that will be in the parent's scope.
+
+ if (the_function.arity === "statement") {
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ name = next_token;
+ enroll(name, "variable", true);
+ the_function.name = name;
+ name.init = true;
+ name.calls = empty();
+ advance();
+ } else if (name === undefined) {
+
+// A function expression may have an optional name.
+
+ if (next_token.identifier) {
+ name = next_token;
+ the_function.name = name;
+ advance();
+ } else {
+ the_function.name = anon;
+ }
+ }
+ } else {
+ name = the_function.name;
+ }
+ the_function.level = functionage.level + 1;
+ if (mega_mode) {
+ warn("unexpected_a", the_function);
+ }
+
+// Don't make functions in loops. It is inefficient, and it can lead to scoping
+// errors.
+
+ if (functionage.loop > 0) {
+ warn("function_in_loop", the_function);
+ }
+
+// Give the function properties for storing its names and for observing the
+// depth of loops and switches.
+
+ the_function.context = empty();
+ the_function.finally = 0;
+ the_function.loop = 0;
+ the_function.switch = 0;
+ the_function.try = 0;
+
+// Push the current function context and establish a new one.
+
+ stack.push(functionage);
+ functions.push(the_function);
+ functionage = the_function;
+ if (the_function.arity !== "statement" && typeof name === "object") {
+ enroll(name, "function", true);
+ name.dead = false;
+ name.init = true;
+ name.used = 1;
+ }
+
+// Parse the parameter list.
+
+ advance("(");
+ token.free = false;
+ token.arity = "function";
+ [functionage.parameters, functionage.signature] = parameter_list();
+ functionage.parameters.forEach(function enroll_parameter(name) {
+ if (name.identifier) {
+ enroll(name, "parameter", false);
+ } else {
+ name.names.forEach(enroll_parameter);
+ }
+ });
+
+// The function's body is a block.
+
+ the_function.block = block("body");
+ if (
+ the_function.arity === "statement"
+ && next_token.line === token.line
+ ) {
+ return stop("unexpected_a", next_token);
+ }
+ if (
+ next_token.id === "."
+ || next_token.id === "?."
+ || next_token.id === "["
+ ) {
+ warn("unexpected_a");
+ }
+
+// Restore the previous context.
+
+ functionage = stack.pop();
+ return the_function;
+}
+
+prefix("function", do_function);
+
+function fart(pl) {
+ advance("=>");
+ const the_fart = token;
+ the_fart.arity = "binary";
+ the_fart.name = "=>";
+ the_fart.level = functionage.level + 1;
+ functions.push(the_fart);
+ if (functionage.loop > 0) {
+ warn("function_in_loop", the_fart);
+ }
+
+// Give the function properties storing its names and for observing the depth
+// of loops and switches.
+
+ the_fart.context = empty();
+ the_fart.finally = 0;
+ the_fart.loop = 0;
+ the_fart.switch = 0;
+ the_fart.try = 0;
+
+// Push the current function context and establish a new one.
+
+ stack.push(functionage);
+ functionage = the_fart;
+ the_fart.parameters = pl[0];
+ the_fart.signature = pl[1];
+ the_fart.parameters.forEach(function (name) {
+ enroll(name, "parameter", true);
+ });
+ if (next_token.id === "{") {
+ warn("expected_a_b", the_fart, "function", "=>");
+ the_fart.block = block("body");
+ } else {
+ the_fart.expression = expression(0);
+ }
+ functionage = stack.pop();
+ return the_fart;
+}
+
+prefix("(", function () {
+ const the_paren = token;
+ let the_value;
+ const cadet = lookahead().id;
+
+// We can distinguish between a parameter list for => and a wrapped expression
+// with one token of lookahead.
+
+ if (
+ next_token.id === ")"
+ || next_token.id === "..."
+ || (next_token.identifier && (cadet === "," || cadet === "="))
+ ) {
+ the_paren.free = false;
+ return fart(parameter_list());
+ }
+ the_paren.free = true;
+ the_value = expression(0);
+ if (the_value.wrapped === true) {
+ warn("unexpected_a", the_paren);
+ }
+ the_value.wrapped = true;
+ advance(")", the_paren);
+ if (next_token.id === "=>") {
+ if (the_value.arity !== "variable") {
+ if (the_value.id === "{" || the_value.id === "[") {
+ warn("expected_a_before_b", the_paren, "function", "(");
+ return stop("expected_a_b", next_token, "{", "=>");
+ }
+ return stop("expected_identifier_a", the_value);
+ }
+ the_paren.expression = [the_value];
+ return fart([the_paren.expression, "(" + the_value.id + ")"]);
+ }
+ return the_value;
+});
+prefix("`", do_tick);
+prefix("{", function () {
+ const the_brace = token;
+ const seen = empty();
+ the_brace.expression = [];
+ if (next_token.id !== "}") {
+ (function member() {
+ let extra;
+ let full;
+ let id;
+ let name = next_token;
+ let value;
+ advance();
+ if (
+ (name.id === "get" || name.id === "set")
+ && next_token.identifier
+ ) {
+ if (!option.getset) {
+ warn("unexpected_a", name);
+ }
+ extra = name.id;
+ full = extra + " " + next_token.id;
+ name = next_token;
+ advance();
+ id = survey(name);
+ if (seen[full] === true || seen[id] === true) {
+ warn("duplicate_a", name);
+ }
+ seen[id] = false;
+ seen[full] = true;
+ } else {
+ id = survey(name);
+ if (typeof seen[id] === "boolean") {
+ warn("duplicate_a", name);
+ }
+ seen[id] = true;
+ }
+ if (name.identifier) {
+ if (next_token.id === "}" || next_token.id === ",") {
+ if (typeof extra === "string") {
+ advance("(");
+ }
+ value = expression(Infinity, true);
+ } else if (next_token.id === "(") {
+ value = do_function({
+ arity: "unary",
+ from: name.from,
+ id: "function",
+ line: name.line,
+ name: (
+ typeof extra === "string"
+ ? extra
+ : id
+ ),
+ thru: name.from
+ });
+ } else {
+ if (typeof extra === "string") {
+ advance("(");
+ }
+ let the_colon = next_token;
+ advance(":");
+ value = expression(0);
+ if (value.id === name.id) {
+ warn("unexpected_a", the_colon, ": " + name.id);
+ }
+ }
+ value.label = name;
+ if (typeof extra === "string") {
+ value.extra = extra;
+ }
+ the_brace.expression.push(value);
+ } else {
+ advance(":");
+ value = expression(0);
+ value.label = name;
+ the_brace.expression.push(value);
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ return member();
+ }
+ }());
+ }
+ advance("}");
+ return the_brace;
+});
+
+stmt(";", function () {
+ warn("unexpected_a", token);
+ return token;
+});
+stmt("{", function () {
+ warn("naked_block", token);
+ return block("naked");
+});
+stmt("break", function () {
+ const the_break = token;
+ let the_label;
+ if (
+ (functionage.loop < 1 && functionage.switch < 1)
+ || functionage.finally > 0
+ ) {
+ warn("unexpected_a", the_break);
+ }
+ the_break.disrupt = true;
+ if (next_token.identifier && token.line === next_token.line) {
+ the_label = functionage.context[next_token.id];
+ if (
+ the_label === undefined
+ || the_label.role !== "label"
+ || the_label.dead
+ ) {
+ warn(
+ (the_label !== undefined && the_label.dead)
+ ? "out_of_scope_a"
+ : "not_label_a"
+ );
+ } else {
+ the_label.used += 1;
+ }
+ the_break.label = next_token;
+ advance();
+ }
+ advance(";");
+ return the_break;
+});
+
+function do_var() {
+ const the_statement = token;
+ const is_const = the_statement.id === "const";
+ the_statement.names = [];
+
+// A program may use var or let, but not both.
+
+ if (!is_const) {
+ if (var_mode === undefined) {
+ var_mode = the_statement.id;
+ } else if (the_statement.id !== var_mode) {
+ warn(
+ "expected_a_b",
+ the_statement,
+ var_mode,
+ the_statement.id
+ );
+ }
+ }
+
+// We don't expect to see variables created in switch statements.
+
+ if (functionage.switch > 0) {
+ warn("var_switch", the_statement);
+ }
+ if (functionage.loop > 0 && the_statement.id === "var") {
+ warn("var_loop", the_statement);
+ }
+ (function next() {
+ if (next_token.id === "{" && the_statement.id !== "var") {
+ const the_brace = next_token;
+ advance("{");
+ (function pair() {
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ const name = next_token;
+ survey(name);
+ advance();
+ if (next_token.id === ":") {
+ advance(":");
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ next_token.label = name;
+ the_statement.names.push(next_token);
+ enroll(next_token, "variable", is_const);
+ advance();
+ the_brace.open = true;
+ } else {
+ the_statement.names.push(name);
+ enroll(name, "variable", is_const);
+ }
+ name.dead = false;
+ name.init = true;
+ if (next_token.id === "=") {
+ advance("=");
+ name.expression = expression();
+ the_brace.open = true;
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ return pair();
+ }
+ }());
+ advance("}");
+ advance("=");
+ the_statement.expression = expression(0);
+ } else if (next_token.id === "[" && the_statement.id !== "var") {
+ const the_bracket = next_token;
+ advance("[");
+ (function element() {
+ let ellipsis;
+ if (next_token.id === "...") {
+ ellipsis = true;
+ advance("...");
+ }
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ const name = next_token;
+ advance();
+ the_statement.names.push(name);
+ enroll(name, "variable", is_const);
+ name.dead = false;
+ name.init = true;
+ if (ellipsis) {
+ name.ellipsis = true;
+ } else {
+ if (next_token.id === "=") {
+ advance("=");
+ name.expression = expression();
+ the_bracket.open = true;
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ return element();
+ }
+ }
+ }());
+ advance("]");
+ advance("=");
+ the_statement.expression = expression(0);
+ } else if (next_token.identifier) {
+ const name = next_token;
+ advance();
+ if (name.id === "ignore") {
+ warn("unexpected_a", name);
+ }
+ enroll(name, "variable", is_const);
+ if (next_token.id === "=" || is_const) {
+ advance("=");
+ name.dead = false;
+ name.init = true;
+ name.expression = expression(0);
+ }
+ the_statement.names.push(name);
+ } else {
+ return stop("expected_identifier_a", next_token);
+ }
+ }());
+ semicolon();
+ return the_statement;
+}
+
+stmt("const", do_var);
+stmt("continue", function () {
+ const the_continue = token;
+ if (functionage.loop < 1 || functionage.finally > 0) {
+ warn("unexpected_a", the_continue);
+ }
+ not_top_level(the_continue);
+ the_continue.disrupt = true;
+ warn("unexpected_a", the_continue);
+ advance(";");
+ return the_continue;
+});
+stmt("debugger", function () {
+ const the_debug = token;
+ if (!option.devel) {
+ warn("unexpected_a", the_debug);
+ }
+ semicolon();
+ return the_debug;
+});
+stmt("delete", function () {
+ const the_token = token;
+ const the_value = expression(0);
+ if (
+ (the_value.id !== "." && the_value.id !== "[")
+ || the_value.arity !== "binary"
+ ) {
+ stop("expected_a_b", the_value, ".", artifact(the_value));
+ }
+ the_token.expression = the_value;
+ semicolon();
+ return the_token;
+});
+stmt("do", function () {
+ const the_do = token;
+ not_top_level(the_do);
+ functionage.loop += 1;
+ the_do.block = block();
+ advance("while");
+ the_do.expression = condition();
+ semicolon();
+ if (the_do.block.disrupt === true) {
+ warn("weird_loop", the_do);
+ }
+ functionage.loop -= 1;
+ return the_do;
+});
+stmt("export", function () {
+ const the_export = token;
+ let the_id;
+ let the_name;
+ let the_thing;
+
+ function export_id() {
+ if (!next_token.identifier) {
+ stop("expected_identifier_a");
+ }
+ the_id = next_token.id;
+ the_name = global.context[the_id];
+ if (the_name === undefined) {
+ warn("unexpected_a");
+ } else {
+ the_name.used += 1;
+ if (exports[the_id] !== undefined) {
+ warn("duplicate_a");
+ }
+ exports[the_id] = the_name;
+ }
+ advance();
+ the_export.expression.push(the_thing);
+ }
+
+ the_export.expression = [];
+ if (next_token.id === "default") {
+ if (exports.default !== undefined) {
+ warn("duplicate_a");
+ }
+ advance("default");
+ the_thing = expression(0);
+ if (
+ the_thing.id !== "("
+ || the_thing.expression[0].id !== "."
+ || the_thing.expression[0].expression.id !== "Object"
+ || the_thing.expression[0].name.id !== "freeze"
+ ) {
+ warn("freeze_exports", the_thing);
+ }
+ if (next_token.id === ";") {
+ semicolon();
+ }
+ exports.default = the_thing;
+ the_export.expression.push(the_thing);
+ } else {
+ if (next_token.id === "function") {
+ warn("freeze_exports");
+ the_thing = statement();
+ the_name = the_thing.name;
+ the_id = the_name.id;
+ the_name.used += 1;
+ if (exports[the_id] !== undefined) {
+ warn("duplicate_a", the_name);
+ }
+ exports[the_id] = the_thing;
+ the_export.expression.push(the_thing);
+ the_thing.statement = false;
+ the_thing.arity = "unary";
+ } else if (
+ next_token.id === "var"
+ || next_token.id === "let"
+ || next_token.id === "const"
+ ) {
+ warn("unexpected_a", next_token);
+ statement();
+ } else if (next_token.id === "{") {
+ advance("{");
+ (function loop() {
+ export_id();
+ if (next_token.id === ",") {
+ advance(",");
+ return loop();
+ }
+ }());
+ advance("}");
+ semicolon();
+ } else {
+ stop("unexpected_a");
+ }
+ }
+ module_mode = true;
+ return the_export;
+});
+stmt("for", function () {
+ let first;
+ const the_for = token;
+ if (!option.for) {
+ warn("unexpected_a", the_for);
+ }
+ not_top_level(the_for);
+ functionage.loop += 1;
+ advance("(");
+ token.free = true;
+ if (next_token.id === ";") {
+ return stop("expected_a_b", the_for, "while (", "for (;");
+ }
+ if (
+ next_token.id === "var"
+ || next_token.id === "let"
+ || next_token.id === "const"
+ ) {
+ return stop("unexpected_a");
+ }
+ first = expression(0);
+ if (first.id === "in") {
+ if (first.expression[0].arity !== "variable") {
+ warn("bad_assignment_a", first.expression[0]);
+ }
+ the_for.name = first.expression[0];
+ the_for.expression = first.expression[1];
+ warn("expected_a_b", the_for, "Object.keys", "for in");
+ } else {
+ the_for.initial = first;
+ advance(";");
+ the_for.expression = expression(0);
+ advance(";");
+ the_for.inc = expression(0);
+ if (the_for.inc.id === "++") {
+ warn("expected_a_b", the_for.inc, "+= 1", "++");
+ }
+ }
+ advance(")");
+ the_for.block = block();
+ if (the_for.block.disrupt === true) {
+ warn("weird_loop", the_for);
+ }
+ functionage.loop -= 1;
+ return the_for;
+});
+stmt("function", do_function);
+stmt("if", function () {
+ let the_else;
+ const the_if = token;
+ the_if.expression = condition();
+ the_if.block = block();
+ if (next_token.id === "else") {
+ advance("else");
+ the_else = token;
+ the_if.else = (
+ next_token.id === "if"
+ ? statement()
+ : block()
+ );
+ if (the_if.block.disrupt === true) {
+ if (the_if.else.disrupt === true) {
+ the_if.disrupt = true;
+ } else {
+ warn("unexpected_a", the_else);
+ }
+ }
+ }
+ return the_if;
+});
+stmt("import", function () {
+ const the_import = token;
+ let name;
+ if (typeof module_mode === "object") {
+ warn("unexpected_directive_a", module_mode, module_mode.directive);
+ }
+ module_mode = true;
+ if (next_token.identifier) {
+ name = next_token;
+ advance();
+ if (name.id === "ignore") {
+ warn("unexpected_a", name);
+ }
+ enroll(name, "variable", true);
+ the_import.name = name;
+ } else {
+ const names = [];
+ advance("{");
+ if (next_token.id !== "}") {
+ while (true) {
+ if (!next_token.identifier) {
+ stop("expected_identifier_a");
+ }
+ name = next_token;
+ advance();
+ if (name.id === "ignore") {
+ warn("unexpected_a", name);
+ }
+ enroll(name, "variable", true);
+ names.push(name);
+ if (next_token.id !== ",") {
+ break;
+ }
+ advance(",");
+ }
+ }
+ advance("}");
+ the_import.name = names;
+ }
+ advance("from");
+ advance("(string)");
+ the_import.import = token;
+ if (!rx_module.test(token.value)) {
+ warn("bad_module_name_a", token);
+ }
+ froms.push(token.value);
+ semicolon();
+ return the_import;
+});
+stmt("let", do_var);
+stmt("return", function () {
+ const the_return = token;
+ not_top_level(the_return);
+ if (functionage.finally > 0) {
+ warn("unexpected_a", the_return);
+ }
+ the_return.disrupt = true;
+ if (next_token.id !== ";" && the_return.line === next_token.line) {
+ the_return.expression = expression(10);
+ }
+ advance(";");
+ return the_return;
+});
+stmt("switch", function () {
+ let dups = [];
+ let last;
+ let stmts;
+ const the_cases = [];
+ let the_disrupt = true;
+ const the_switch = token;
+ not_top_level(the_switch);
+ if (functionage.finally > 0) {
+ warn("unexpected_a", the_switch);
+ }
+ functionage.switch += 1;
+ advance("(");
+ token.free = true;
+ the_switch.expression = expression(0);
+ the_switch.block = the_cases;
+ advance(")");
+ advance("{");
+ (function major() {
+ const the_case = next_token;
+ the_case.arity = "statement";
+ the_case.expression = [];
+ (function minor() {
+ advance("case");
+ token.switch = true;
+ const exp = expression(0);
+ if (dups.some(function (thing) {
+ return are_similar(thing, exp);
+ })) {
+ warn("unexpected_a", exp);
+ }
+ dups.push(exp);
+ the_case.expression.push(exp);
+ advance(":");
+ if (next_token.id === "case") {
+ return minor();
+ }
+ }());
+ stmts = statements();
+ if (stmts.length < 1) {
+ warn("expected_statements_a");
+ return;
+ }
+ the_case.block = stmts;
+ the_cases.push(the_case);
+ last = stmts[stmts.length - 1];
+ if (last.disrupt) {
+ if (last.id === "break" && last.label === undefined) {
+ the_disrupt = false;
+ }
+ } else {
+ warn(
+ "expected_a_before_b",
+ next_token,
+ "break;",
+ artifact(next_token)
+ );
+ }
+ if (next_token.id === "case") {
+ return major();
+ }
+ }());
+ dups = undefined;
+ if (next_token.id === "default") {
+ const the_default = next_token;
+ advance("default");
+ token.switch = true;
+ advance(":");
+ the_switch.else = statements();
+ if (the_switch.else.length < 1) {
+ warn("unexpected_a", the_default);
+ the_disrupt = false;
+ } else {
+ const the_last = the_switch.else[the_switch.else.length - 1];
+ if (the_last.id === "break" && the_last.label === undefined) {
+ warn("unexpected_a", the_last);
+ the_last.disrupt = false;
+ }
+ the_disrupt = the_disrupt && the_last.disrupt;
+ }
+ } else {
+ the_disrupt = false;
+ }
+ advance("}", the_switch);
+ functionage.switch -= 1;
+ the_switch.disrupt = the_disrupt;
+ return the_switch;
+});
+stmt("throw", function () {
+ const the_throw = token;
+ the_throw.disrupt = true;
+ the_throw.expression = expression(10);
+ semicolon();
+ if (functionage.try > 0) {
+ warn("unexpected_a", the_throw);
+ }
+ return the_throw;
+});
+stmt("try", function () {
+ let the_catch;
+ let the_disrupt;
+ const the_try = token;
+ if (functionage.try > 0) {
+ warn("unexpected_a", the_try);
+ }
+ functionage.try += 1;
+ the_try.block = block();
+ the_disrupt = the_try.block.disrupt;
+ if (next_token.id === "catch") {
+ let ignored = "ignore";
+ the_catch = next_token;
+ the_try.catch = the_catch;
+ advance("catch");
+ if (next_token.id === "(") {
+ advance("(");
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ if (next_token.id !== "ignore") {
+ ignored = undefined;
+ the_catch.name = next_token;
+ enroll(next_token, "exception", true);
+ }
+ advance();
+ advance(")");
+ }
+ the_catch.block = block(ignored);
+ if (the_catch.block.disrupt !== true) {
+ the_disrupt = false;
+ }
+ } else {
+ warn(
+ "expected_a_before_b",
+ next_token,
+ "catch",
+ artifact(next_token)
+ );
+
+ }
+ if (next_token.id === "finally") {
+ functionage.finally += 1;
+ advance("finally");
+ the_try.else = block();
+ the_disrupt = the_try.else.disrupt;
+ functionage.finally -= 1;
+ }
+ the_try.disrupt = the_disrupt;
+ functionage.try -= 1;
+ return the_try;
+});
+stmt("var", do_var);
+stmt("while", function () {
+ const the_while = token;
+ not_top_level(the_while);
+ functionage.loop += 1;
+ the_while.expression = condition();
+ the_while.block = block();
+ if (the_while.block.disrupt === true) {
+ warn("weird_loop", the_while);
+ }
+ functionage.loop -= 1;
+ return the_while;
+});
+stmt("with", function () {
+ stop("unexpected_a", token);
+});
+
+ternary("?", ":");
+
+// Ambulation of the parse tree.
+
+function action(when) {
+
+// Produce a function that will register task functions that will be called as
+// the tree is traversed.
+
+ return function (arity, id, task) {
+ let a_set = when[arity];
+ let i_set;
+
+// The id parameter is optional. If excluded, the task will be applied to all
+// ids.
+
+ if (typeof id !== "string") {
+ task = id;
+ id = "(all)";
+ }
+
+// If this arity has no registrations yet, then create a set object to hold
+// them.
+
+ if (a_set === undefined) {
+ a_set = empty();
+ when[arity] = a_set;
+ }
+
+// If this id has no registrations yet, then create a set array to hold them.
+
+ i_set = a_set[id];
+ if (i_set === undefined) {
+ i_set = [];
+ a_set[id] = i_set;
+ }
+
+// Register the task with the arity and the id.
+
+ i_set.push(task);
+ };
+}
+
+function amble(when) {
+
+// Produce a function that will act on the tasks registered by an action
+// function while walking the tree.
+
+ return function (the_token) {
+
+// Given a task set that was built by an action function, run all of the
+// relevant tasks on the token.
+
+ let a_set = when[the_token.arity];
+ let i_set;
+
+// If there are tasks associated with the token's arity...
+
+ if (a_set !== undefined) {
+
+// If there are tasks associated with the token's id...
+
+ i_set = a_set[the_token.id];
+ if (i_set !== undefined) {
+ i_set.forEach(function (task) {
+ return task(the_token);
+ });
+ }
+
+// If there are tasks for all ids.
+
+ i_set = a_set["(all)"];
+ if (i_set !== undefined) {
+ i_set.forEach(function (task) {
+ return task(the_token);
+ });
+ }
+ }
+ };
+}
+
+const posts = empty();
+const pres = empty();
+const preaction = action(pres);
+const postaction = action(posts);
+const preamble = amble(pres);
+const postamble = amble(posts);
+
+function walk_expression(thing) {
+ if (thing) {
+ if (Array.isArray(thing)) {
+ thing.forEach(walk_expression);
+ } else {
+ preamble(thing);
+ walk_expression(thing.expression);
+ if (thing.id === "function") {
+ walk_statement(thing.block);
+ }
+ if (thing.arity === "pre" || thing.arity === "post") {
+ warn("unexpected_a", thing);
+ } else if (
+ thing.arity === "statement"
+ || thing.arity === "assignment"
+ ) {
+ warn("unexpected_statement_a", thing);
+ }
+ postamble(thing);
+ }
+ }
+}
+
+function walk_statement(thing) {
+ if (thing) {
+ if (Array.isArray(thing)) {
+ thing.forEach(walk_statement);
+ } else {
+ preamble(thing);
+ walk_expression(thing.expression);
+ if (thing.arity === "binary") {
+ if (thing.id !== "(") {
+ warn("unexpected_expression_a", thing);
+ }
+ } else if (
+ thing.arity !== "statement"
+ && thing.arity !== "assignment"
+ ) {
+ warn("unexpected_expression_a", thing);
+ }
+ walk_statement(thing.block);
+ walk_statement(thing.else);
+ postamble(thing);
+ }
+ }
+}
+
+function lookup(thing) {
+ if (thing.arity === "variable") {
+
+// Look up the variable in the current context.
+
+ let the_variable = functionage.context[thing.id];
+
+// If it isn't local, search all the other contexts. If there are name
+// collisions, take the most recent.
+
+ if (the_variable === undefined) {
+ stack.forEach(function (outer) {
+ const a_variable = outer.context[thing.id];
+ if (
+ a_variable !== undefined
+ && a_variable.role !== "label"
+ ) {
+ the_variable = a_variable;
+ }
+ });
+
+// If it isn't in any of those either, perhaps it is a predefined global.
+// If so, add it to the global context.
+
+ if (the_variable === undefined) {
+ if (declared_globals[thing.id] === undefined) {
+ warn("undeclared_a", thing);
+ return;
+ }
+ the_variable = {
+ dead: false,
+ parent: global,
+ id: thing.id,
+ init: true,
+ role: "variable",
+ used: 0,
+ writable: false
+ };
+ global.context[thing.id] = the_variable;
+ }
+ the_variable.closure = true;
+ functionage.context[thing.id] = the_variable;
+ } else if (the_variable.role === "label") {
+ warn("label_a", thing);
+ }
+ if (
+ the_variable.dead
+ && (
+ the_variable.calls === undefined
+ || the_variable.calls[functionage.name.id] === undefined
+ )
+ ) {
+ warn("out_of_scope_a", thing);
+ }
+ return the_variable;
+ }
+}
+
+function subactivate(name) {
+ name.init = true;
+ name.dead = false;
+ blockage.live.push(name);
+}
+
+function preaction_function(thing) {
+ if (thing.arity === "statement" && blockage.body !== true) {
+ warn("unexpected_a", thing);
+ }
+ stack.push(functionage);
+ block_stack.push(blockage);
+ functionage = thing;
+ blockage = thing;
+ thing.live = [];
+ if (typeof thing.name === "object") {
+ thing.name.dead = false;
+ thing.name.init = true;
+ }
+ if (thing.extra === "get") {
+ if (thing.parameters.length !== 0) {
+ warn("bad_get", thing);
+ }
+ } else if (thing.extra === "set") {
+ if (thing.parameters.length !== 1) {
+ warn("bad_set", thing);
+ }
+ }
+ thing.parameters.forEach(function (name) {
+ walk_expression(name.expression);
+ if (name.id === "{" || name.id === "[") {
+ name.names.forEach(subactivate);
+ } else {
+ name.dead = false;
+ name.init = true;
+ }
+ });
+}
+
+function bitwise_check(thing) {
+ if (!option.bitwise && bitwiseop[thing.id] === true) {
+ warn("unexpected_a", thing);
+ }
+ if (
+ thing.id !== "("
+ && thing.id !== "&&"
+ && thing.id !== "||"
+ && thing.id !== "="
+ && Array.isArray(thing.expression)
+ && thing.expression.length === 2
+ && (
+ relationop[thing.expression[0].id] === true
+ || relationop[thing.expression[1].id] === true
+ )
+ ) {
+ warn("unexpected_a", thing);
+ }
+}
+
+function pop_block() {
+ blockage.live.forEach(function (name) {
+ name.dead = true;
+ });
+ delete blockage.live;
+ blockage = block_stack.pop();
+}
+
+function activate(name) {
+ name.dead = false;
+ if (name.expression !== undefined) {
+ walk_expression(name.expression);
+ if (name.id === "{" || name.id === "[") {
+ name.names.forEach(subactivate);
+ } else {
+ name.init = true;
+ }
+ }
+ blockage.live.push(name);
+}
+
+function action_var(thing) {
+ thing.names.forEach(activate);
+}
+
+preaction("assignment", bitwise_check);
+preaction("binary", bitwise_check);
+preaction("binary", function (thing) {
+ if (relationop[thing.id] === true) {
+ const left = thing.expression[0];
+ const right = thing.expression[1];
+ if (left.id === "NaN" || right.id === "NaN") {
+ warn("number_isNaN", thing);
+ } else if (left.id === "typeof") {
+ if (right.id !== "(string)") {
+ if (right.id !== "typeof") {
+ warn("expected_string_a", right);
+ }
+ } else {
+ const value = right.value;
+ if (value === "null" || value === "undefined") {
+ warn("unexpected_typeof_a", right, value);
+ } else if (
+ value !== "boolean"
+ && value !== "function"
+ && value !== "number"
+ && value !== "object"
+ && value !== "string"
+ && value !== "symbol"
+ ) {
+ warn("expected_type_string_a", right, value);
+ }
+ }
+ }
+ }
+});
+preaction("binary", "==", function (thing) {
+ warn("expected_a_b", thing, "===", "==");
+});
+preaction("binary", "!=", function (thing) {
+ warn("expected_a_b", thing, "!==", "!=");
+});
+preaction("binary", "=>", preaction_function);
+preaction("binary", "||", function (thing) {
+ thing.expression.forEach(function (thang) {
+ if (thang.id === "&&" && !thang.wrapped) {
+ warn("and", thang);
+ }
+ });
+});
+preaction("binary", "(", function (thing) {
+ const left = thing.expression[0];
+ if (
+ left.identifier
+ && functionage.context[left.id] === undefined
+ && typeof functionage.name === "object"
+ ) {
+ const parent = functionage.name.parent;
+ if (parent) {
+ const left_variable = parent.context[left.id];
+ if (
+ left_variable !== undefined
+ && left_variable.dead
+ && left_variable.parent === parent
+ && left_variable.calls !== undefined
+ && left_variable.calls[functionage.name.id] !== undefined
+ ) {
+ left_variable.dead = false;
+ }
+ }
+ }
+});
+preaction("binary", "in", function (thing) {
+ warn("infix_in", thing);
+});
+preaction("binary", "instanceof", function (thing) {
+ warn("unexpected_a", thing);
+});
+preaction("binary", ".", function (thing) {
+ if (thing.expression.new) {
+ thing.new = true;
+ }
+});
+preaction("statement", "{", function (thing) {
+ block_stack.push(blockage);
+ blockage = thing;
+ thing.live = [];
+});
+preaction("statement", "for", function (thing) {
+ if (thing.name !== undefined) {
+ const the_variable = lookup(thing.name);
+ if (the_variable !== undefined) {
+ the_variable.init = true;
+ if (!the_variable.writable) {
+ warn("bad_assignment_a", thing.name);
+ }
+ }
+ }
+ walk_statement(thing.initial);
+});
+preaction("statement", "function", preaction_function);
+preaction("unary", "~", bitwise_check);
+preaction("unary", "function", preaction_function);
+preaction("variable", function (thing) {
+ const the_variable = lookup(thing);
+ if (the_variable !== undefined) {
+ thing.variable = the_variable;
+ the_variable.used += 1;
+ }
+});
+
+function init_variable(name) {
+ const the_variable = lookup(name);
+ if (the_variable !== undefined) {
+ if (the_variable.writable) {
+ the_variable.init = true;
+ return;
+ }
+ }
+ warn("bad_assignment_a", name);
+}
+
+postaction("assignment", "+=", function (thing) {
+ let right = thing.expression[1];
+ if (right.constant) {
+ if (
+ right.value === ""
+ || (right.id === "(number)" && right.value === "0")
+ || right.id === "(boolean)"
+ || right.id === "null"
+ || right.id === "undefined"
+ || Number.isNaN(right.value)
+ ) {
+ warn("unexpected_a", right);
+ }
+ }
+});
+postaction("assignment", function (thing) {
+
+// Assignment using = sets the init property of a variable. No other assignment
+// operator can do this. A = token keeps that variable (or array of variables
+// in case of destructuring) in its name property.
+
+ const lvalue = thing.expression[0];
+ if (thing.id === "=") {
+ if (thing.names !== undefined) {
+ if (Array.isArray(thing.names)) {
+ thing.names.forEach(init_variable);
+ } else {
+ init_variable(thing.names);
+ }
+ } else {
+ if (lvalue.id === "[" || lvalue.id === "{") {
+ lvalue.expression.forEach(function (thing) {
+ if (thing.variable) {
+ thing.variable.init = true;
+ }
+ });
+ } else if (
+ lvalue.id === "."
+ && thing.expression[1].id === "undefined"
+ ) {
+ warn(
+ "expected_a_b",
+ lvalue.expression,
+ "delete",
+ "undefined"
+ );
+ }
+ }
+ } else {
+ if (lvalue.arity === "variable") {
+ if (!lvalue.variable || lvalue.variable.writable !== true) {
+ warn("bad_assignment_a", lvalue);
+ }
+ }
+ const right = syntax[thing.expression[1].id];
+ if (
+ right !== undefined
+ && (
+ right.id === "function"
+ || right.id === "=>"
+ || (
+ right.constant
+ && right.id !== "(number)"
+ && (right.id !== "(string)" || thing.id !== "+=")
+ )
+ )
+ ) {
+ warn("unexpected_a", thing.expression[1]);
+ }
+ }
+});
+
+function postaction_function(thing) {
+ delete functionage.finally;
+ delete functionage.loop;
+ delete functionage.switch;
+ delete functionage.try;
+ functionage = stack.pop();
+ if (thing.wrapped) {
+ warn("unexpected_parens", thing);
+ }
+ return pop_block();
+}
+
+postaction("binary", function (thing) {
+ let right;
+ if (relationop[thing.id]) {
+ if (
+ is_weird(thing.expression[0])
+ || is_weird(thing.expression[1])
+ || are_similar(thing.expression[0], thing.expression[1])
+ || (
+ thing.expression[0].constant === true
+ && thing.expression[1].constant === true
+ )
+ ) {
+ warn("weird_relation_a", thing);
+ }
+ }
+ if (thing.id === "+") {
+ if (!option.convert) {
+ if (thing.expression[0].value === "") {
+ warn("expected_a_b", thing, "String(...)", "\"\" +");
+ } else if (thing.expression[1].value === "") {
+ warn("expected_a_b", thing, "String(...)", "+ \"\"");
+ }
+ }
+ } else if (thing.id === "[") {
+ if (thing.expression[0].id === "window") {
+ warn("weird_expression_a", thing, "window[...]");
+ }
+ if (thing.expression[0].id === "self") {
+ warn("weird_expression_a", thing, "self[...]");
+ }
+ } else if (thing.id === "." || thing.id === "?.") {
+ if (thing.expression.id === "RegExp") {
+ warn("weird_expression_a", thing);
+ }
+ } else if (thing.id !== "=>" && thing.id !== "(") {
+ right = thing.expression[1];
+ if (
+ (thing.id === "+" || thing.id === "-")
+ && right.id === thing.id
+ && right.arity === "unary"
+ && !right.wrapped
+ ) {
+ warn("wrap_unary", right);
+ }
+ if (
+ thing.expression[0].constant === true
+ && right.constant === true
+ ) {
+ thing.constant = true;
+ }
+ }
+});
+postaction("binary", "&&", function (thing) {
+ if (
+ is_weird(thing.expression[0])
+ || are_similar(thing.expression[0], thing.expression[1])
+ || thing.expression[0].constant === true
+ || thing.expression[1].constant === true
+ ) {
+ warn("weird_condition_a", thing);
+ }
+});
+postaction("binary", "||", function (thing) {
+ if (
+ is_weird(thing.expression[0])
+ || are_similar(thing.expression[0], thing.expression[1])
+ || thing.expression[0].constant === true
+ ) {
+ warn("weird_condition_a", thing);
+ }
+});
+postaction("binary", "=>", postaction_function);
+postaction("binary", "(", function (thing) {
+ let left = thing.expression[0];
+ let the_new;
+ let arg;
+ if (left.id === "new") {
+ the_new = left;
+ left = left.expression;
+ }
+ if (left.id === "function") {
+ if (!thing.wrapped) {
+ warn("wrap_immediate", thing);
+ }
+ } else if (left.identifier) {
+ if (the_new !== undefined) {
+ if (
+ left.id[0] > "Z"
+ || left.id === "Boolean"
+ || left.id === "Number"
+ || left.id === "String"
+ || left.id === "Symbol"
+ ) {
+ warn("unexpected_a", the_new);
+ } else if (left.id === "Function") {
+ if (!option.eval) {
+ warn("unexpected_a", left, "new Function");
+ }
+ } else if (left.id === "Array") {
+ arg = thing.expression;
+ if (arg.length !== 2 || arg[1].id === "(string)") {
+ warn("expected_a_b", left, "[]", "new Array");
+ }
+ } else if (left.id === "Object") {
+ warn(
+ "expected_a_b",
+ left,
+ "Object.create(null)",
+ "new Object"
+ );
+ }
+ } else {
+ if (
+ left.id[0] >= "A"
+ && left.id[0] <= "Z"
+ && left.id !== "Boolean"
+ && left.id !== "Number"
+ && left.id !== "String"
+ && left.id !== "Symbol"
+ ) {
+ warn(
+ "expected_a_before_b",
+ left,
+ "new",
+ artifact(left)
+ );
+ }
+ }
+ } else if (left.id === ".") {
+ let cack = the_new !== undefined;
+ if (left.expression.id === "Date" && left.name.id === "UTC") {
+ cack = !cack;
+ }
+ if (rx_cap.test(left.name.id) !== cack) {
+ if (the_new !== undefined) {
+ warn("unexpected_a", the_new);
+ } else {
+ warn(
+ "expected_a_before_b",
+ left.expression,
+ "new",
+ left.name.id
+ );
+ }
+ }
+ if (left.name.id === "getTime") {
+ const paren = left.expression;
+ if (paren.id === "(") {
+ const array = paren.expression;
+ if (array.length === 1) {
+ const new_date = array[0];
+ if (
+ new_date.id === "new"
+ && new_date.expression.id === "Date"
+ ) {
+ warn(
+ "expected_a_b",
+ new_date,
+ "Date.now()",
+ "new Date().getTime()"
+ );
+ }
+ }
+ }
+ }
+ }
+});
+postaction("binary", "[", function (thing) {
+ if (thing.expression[0].id === "RegExp") {
+ warn("weird_expression_a", thing);
+ }
+ if (is_weird(thing.expression[1])) {
+ warn("weird_expression_a", thing.expression[1]);
+ }
+});
+postaction("statement", "{", pop_block);
+postaction("statement", "const", action_var);
+postaction("statement", "export", top_level_only);
+postaction("statement", "for", function (thing) {
+ walk_statement(thing.inc);
+});
+postaction("statement", "function", postaction_function);
+postaction("statement", "import", function (the_thing) {
+ const name = the_thing.name;
+ if (Array.isArray(name)) {
+ name.forEach(function (name) {
+ name.dead = false;
+ name.init = true;
+ blockage.live.push(name);
+ });
+ } else {
+ name.dead = false;
+ name.init = true;
+ blockage.live.push(name);
+ }
+ return top_level_only(the_thing);
+});
+postaction("statement", "let", action_var);
+postaction("statement", "try", function (thing) {
+ if (thing.catch !== undefined) {
+ const the_name = thing.catch.name;
+ if (the_name !== undefined) {
+ const the_variable = functionage.context[the_name.id];
+ the_variable.dead = false;
+ the_variable.init = true;
+ }
+ walk_statement(thing.catch.block);
+ }
+});
+postaction("statement", "var", action_var);
+postaction("ternary", function (thing) {
+ if (
+ is_weird(thing.expression[0])
+ || thing.expression[0].constant === true
+ || are_similar(thing.expression[1], thing.expression[2])
+ ) {
+ warn("unexpected_a", thing);
+ } else if (are_similar(thing.expression[0], thing.expression[1])) {
+ warn("expected_a_b", thing, "||", "?");
+ } else if (are_similar(thing.expression[0], thing.expression[2])) {
+ warn("expected_a_b", thing, "&&", "?");
+ } else if (
+ thing.expression[1].id === "true"
+ && thing.expression[2].id === "false"
+ ) {
+ warn("expected_a_b", thing, "!!", "?");
+ } else if (
+ thing.expression[1].id === "false"
+ && thing.expression[2].id === "true"
+ ) {
+ warn("expected_a_b", thing, "!", "?");
+ } else if (
+ thing.expression[0].wrapped !== true
+ && (
+ thing.expression[0].id === "||"
+ || thing.expression[0].id === "&&"
+ )
+ ) {
+ warn("wrap_condition", thing.expression[0]);
+ }
+});
+postaction("unary", function (thing) {
+ if (thing.id === "`") {
+ if (thing.expression.every(function (thing) {
+ return thing.constant;
+ })) {
+ thing.constant = true;
+ }
+ } else if (thing.id === "!") {
+ if (thing.expression.constant === true) {
+ warn("unexpected_a", thing);
+ }
+ } else if (thing.id === "!!") {
+ if (!option.convert) {
+ warn("expected_a_b", thing, "Boolean(...)", "!!");
+ }
+ } else if (
+ thing.id !== "["
+ && thing.id !== "{"
+ && thing.id !== "function"
+ && thing.id !== "new"
+ ) {
+ if (thing.expression.constant === true) {
+ thing.constant = true;
+ }
+ }
+});
+postaction("unary", "function", postaction_function);
+postaction("unary", "+", function (thing) {
+ if (!option.convert) {
+ warn("expected_a_b", thing, "Number(...)", "+");
+ }
+ const right = thing.expression;
+ if (right.id === "(" && right.expression[0].id === "new") {
+ warn("unexpected_a_before_b", thing, "+", "new");
+ } else if (
+ right.constant
+ || right.id === "{"
+ || (right.id === "[" && right.arity !== "binary")
+ ) {
+ warn("unexpected_a", thing, "+");
+ }
+});
+
+function delve(the_function) {
+ Object.keys(the_function.context).forEach(function (id) {
+ if (id !== "ignore") {
+ const name = the_function.context[id];
+ if (name.parent === the_function) {
+ if (
+ name.used === 0
+ && (
+ name.role !== "function"
+ || name.parent.arity !== "unary"
+ )
+ ) {
+ warn("unused_a", name);
+ } else if (!name.init) {
+ warn("uninitialized_a", name);
+ }
+ }
+ }
+ });
+}
+
+function uninitialized_and_unused() {
+
+// Delve into the functions looking for variables that were not initialized
+// or used. If the file imports or exports, then its global object is also
+// delved.
+
+ if (module_mode === true || option.node) {
+ delve(global);
+ }
+ functions.forEach(delve);
+}
+
+// Go through the token list, looking at usage of whitespace.
+
+function whitage() {
+ let closer = "(end)";
+ let free = false;
+ let left = global;
+ let margin = 0;
+ let nr_comments_skipped = 0;
+ let open = true;
+ let opening = true;
+ let right;
+
+ function pop() {
+ const previous = stack.pop();
+ closer = previous.closer;
+ free = previous.free;
+ margin = previous.margin;
+ open = previous.open;
+ opening = previous.opening;
+ }
+
+ function push() {
+ stack.push({
+ closer,
+ free,
+ margin,
+ open,
+ opening
+ });
+ }
+
+ function expected_at(at) {
+ warn(
+ "expected_a_at_b_c",
+ right,
+ artifact(right),
+ fudge + at,
+ artifact_column(right)
+ );
+ }
+
+ function at_margin(fit) {
+ const at = margin + fit;
+ if (right.from !== at) {
+ return expected_at(at);
+ }
+ }
+
+ function no_space_only() {
+ if (
+ left.id !== "(global)"
+ && left.nr + 1 === right.nr
+ && (
+ left.line !== right.line
+ || left.thru !== right.from
+ )
+ ) {
+ warn(
+ "unexpected_space_a_b",
+ right,
+ artifact(left),
+ artifact(right)
+ );
+ }
+ }
+
+ function no_space() {
+ if (left.line === right.line) {
+ if (left.thru !== right.from && nr_comments_skipped === 0) {
+ warn(
+ "unexpected_space_a_b",
+ right,
+ artifact(left),
+ artifact(right)
+ );
+ }
+ } else {
+ if (open) {
+ const at = (
+ free
+ ? margin
+ : margin + 8
+ );
+ if (right.from < at) {
+ expected_at(at);
+ }
+ } else {
+ if (right.from !== margin + 8) {
+ expected_at(margin + 8);
+ }
+ }
+ }
+ }
+
+ function one_space_only() {
+ if (left.line !== right.line || left.thru + 1 !== right.from) {
+ warn(
+ "expected_space_a_b",
+ right,
+ artifact(left),
+ artifact(right)
+ );
+ }
+ }
+
+ function one_space() {
+ if (left.line === right.line || !open) {
+ if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
+ warn(
+ "expected_space_a_b",
+ right,
+ artifact(left),
+ artifact(right)
+ );
+ }
+ } else {
+ if (right.from !== margin) {
+ expected_at(margin);
+ }
+ }
+ }
+
+ stack = [];
+ tokens.forEach(function (the_token) {
+ right = the_token;
+ if (right.id === "(comment)" || right.id === "(end)") {
+ nr_comments_skipped += 1;
+ } else {
+
+// If left is an opener and right is not the closer, then push the previous
+// state. If the token following the opener is on the next line, then this is
+// an open form. If the tokens are on the same line, then it is a closed form.
+// Open form is more readable, with each item (statement, argument, parameter,
+// etc) starting on its own line. Closed form is more compact. Statement blocks
+// are always in open form.
+
+ const new_closer = opener[left.id];
+ if (typeof new_closer === "string") {
+ if (new_closer !== right.id) {
+ opening = left.open || (left.line !== right.line);
+ push();
+ closer = new_closer;
+ if (opening) {
+ free = closer === ")" && left.free;
+ open = true;
+ margin += 4;
+ if (right.role === "label") {
+ if (right.from !== 0) {
+ expected_at(0);
+ }
+ } else if (right.switch) {
+ at_margin(-4);
+ } else {
+ at_margin(0);
+ }
+ } else {
+ if (right.statement || right.role === "label") {
+ warn(
+ "expected_line_break_a_b",
+ right,
+ artifact(left),
+ artifact(right)
+ );
+ }
+ free = false;
+ open = false;
+ no_space_only();
+ }
+ } else {
+
+// If left and right are opener and closer, then the placement of right depends
+// on the openness. Illegal pairs (like '{]') have already been detected.
+
+ if (left.line === right.line) {
+ no_space();
+ } else {
+ at_margin(0);
+ }
+ }
+ } else {
+ if (right.statement === true) {
+ if (left.id === "else") {
+ one_space_only();
+ } else {
+ at_margin(0);
+ open = false;
+ }
+
+// If right is a closer, then pop the previous state.
+
+ } else if (right.id === closer) {
+ pop();
+ if (opening && right.id !== ";") {
+ at_margin(0);
+ } else {
+ no_space_only();
+ }
+ } else {
+
+// Left is not an opener, and right is not a closer.
+// The nature of left and right will determine the space between them.
+
+// If left is ',' or ';' or right is a statement then if open,
+// right must go at the margin, or if closed, a space between.
+
+ if (right.switch) {
+ at_margin(-4);
+ } else if (right.role === "label") {
+ if (right.from !== 0) {
+ expected_at(0);
+ }
+ } else if (left.id === ",") {
+ if (!open || (
+ (free || closer === "]")
+ && left.line === right.line
+ )) {
+ one_space();
+ } else {
+ at_margin(0);
+ }
+
+// If right is a ternary operator, line it up on the margin.
+
+ } else if (right.arity === "ternary") {
+ if (open) {
+ at_margin(0);
+ } else {
+ warn("use_open", right);
+ }
+ } else if (
+ right.arity === "binary"
+ && right.id === "("
+ && free
+ ) {
+ no_space();
+ } else if (
+ left.id === "."
+ || left.id === "?."
+ || left.id === "..."
+ || right.id === ","
+ || right.id === ";"
+ || right.id === ":"
+ || (
+ right.arity === "binary"
+ && (right.id === "(" || right.id === "[")
+ )
+ || (
+ right.arity === "function"
+ && left.id !== "function"
+ )
+ ) {
+ no_space_only();
+ } else if (right.id === "." || right.id === "?.") {
+ no_space_only();
+ } else if (left.id === ";") {
+ if (open) {
+ at_margin(0);
+ } else {
+ one_space();
+ }
+ } else if (
+ left.arity === "ternary"
+ || left.id === "case"
+ || left.id === "catch"
+ || left.id === "else"
+ || left.id === "finally"
+ || left.id === "while"
+ || right.id === "catch"
+ || right.id === "else"
+ || right.id === "finally"
+ || (right.id === "while" && !right.statement)
+ || (left.id === ")" && right.id === "{")
+ ) {
+ one_space_only();
+ } else if (
+ left.id === "var"
+ || left.id === "const"
+ || left.id === "let"
+ ) {
+ push();
+ closer = ";";
+ free = false;
+ open = left.open;
+ if (open) {
+ margin = margin + 4;
+ at_margin(0);
+ } else {
+ one_space_only();
+ }
+ } else if (
+
+// There is a space between left and right.
+
+ spaceop[left.id] === true
+ || spaceop[right.id] === true
+ || (
+ left.arity === "binary"
+ && (left.id === "+" || left.id === "-")
+ )
+ || (
+ right.arity === "binary"
+ && (right.id === "+" || right.id === "-")
+ )
+ || left.id === "function"
+ || left.id === ":"
+ || (
+ (
+ left.identifier
+ || left.id === "(string)"
+ || left.id === "(number)"
+ )
+ && (
+ right.identifier
+ || right.id === "(string)"
+ || right.id === "(number)"
+ )
+ )
+ || (left.arity === "statement" && right.id !== ";")
+ ) {
+ one_space();
+ } else if (left.arity === "unary" && left.id !== "`") {
+ no_space_only();
+ }
+ }
+ }
+ nr_comments_skipped = 0;
+ delete left.calls;
+ delete left.dead;
+ delete left.free;
+ delete left.init;
+ delete left.open;
+ delete left.used;
+ left = right;
+ }
+ });
+}
+
+// The jslint function itself.
+
+export default Object.freeze(function jslint(
+ source = "",
+ option_object = empty(),
+ global_array = []
+) {
+ try {
+ warnings = [];
+ option = Object.assign(empty(), option_object);
+ anon = "anonymous";
+ block_stack = [];
+ declared_globals = empty();
+ directive_mode = true;
+ directives = [];
+ early_stop = true;
+ exports = empty();
+ froms = [];
+ fudge = (
+ option.fudge
+ ? 1
+ : 0
+ );
+ functions = [];
+ global = {
+ id: "(global)",
+ body: true,
+ context: empty(),
+ from: 0,
+ level: 0,
+ line: 0,
+ live: [],
+ loop: 0,
+ switch: 0,
+ thru: 0
+ };
+ blockage = global;
+ functionage = global;
+ json_mode = false;
+ mega_mode = false;
+ module_mode = false;
+ next_token = global;
+ property = empty();
+ shebang = false;
+ stack = [];
+ tenure = undefined;
+ token = global;
+ token_nr = 0;
+ var_mode = undefined;
+ populate(standard, declared_globals, false);
+ populate(global_array, declared_globals, false);
+ Object.keys(option).forEach(function (name) {
+ if (option[name] === true) {
+ const allowed = allowed_option[name];
+ if (Array.isArray(allowed)) {
+ populate(allowed, declared_globals, false);
+ }
+ }
+ });
+ tokenize(source);
+ advance();
+ if (json_mode) {
+ tree = json_value();
+ advance("(end)");
+ } else {
+
+// Because browsers encourage combining of script files, the first token might
+// be a semicolon to defend against a missing semicolon in the preceding file.
+
+ if (option.browser) {
+ if (next_token.id === ";") {
+ advance(";");
+ }
+ } else {
+
+// If we are not in a browser, then the file form of strict pragma may be used.
+
+ if (
+ next_token.value === "use strict"
+ ) {
+ advance("(string)");
+ advance(";");
+ }
+ }
+ tree = statements();
+ advance("(end)");
+ functionage = global;
+ walk_statement(tree);
+ if (warnings.length === 0) {
+ uninitialized_and_unused();
+ if (!option.white) {
+ whitage();
+ }
+ }
+ }
+ if (!option.browser) {
+ directives.forEach(function (comment) {
+ if (comment.directive === "global") {
+ warn("missing_browser", comment);
+ }
+ });
+ }
+ early_stop = false;
+ } catch (e) {
+ if (e.name !== "JSLintError") {
+ warnings.push(e);
+ }
+ }
+ return {
+ directives,
+ edition: "2019-01-31",
+ exports,
+ froms,
+ functions,
+ global,
+ id: "(JSLint)",
+ json: json_mode,
+ lines,
+ module: module_mode === true,
+ ok: warnings.length === 0 && !early_stop,
+ option,
+ property,
+ shebang: (
+ shebang
+ ? lines[0]
+ : undefined
+ ),
+ stop: early_stop,
+ tokens,
+ tree,
+ warnings: warnings.sort(function (a, b) {
+ return a.line - b.line || a.column - b.column;
+ })
+ };
+});
diff --git a/lib/jslint-latest.js b/lib/jslint-latest.js
index 931453d..e7b05db 100755
--- a/lib/jslint-latest.js
+++ b/lib/jslint-latest.js
@@ -1,5 +1,5 @@
// jslint.js
-// 2015-10-29
+// 2019-01-31
// Copyright (c) 2015 Douglas Crockford (www.JSLint.com)
// Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -22,7 +22,8 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
-// jslint is a function that takes 3 arguments:
+// jslint(source, option_object, global_array) is a function that takes 3
+// arguments. The second two arguments are optional.
// source A text to analyze, a string or an array of strings.
// option_object An object whose keys correspond to option names.
@@ -33,13 +34,15 @@
// of valuable information. It can be used to generate reports. The object
// contains:
+// directives: an array of directive comment tokens.
// edition: the version of JSLint that did the analysis.
+// exports: the names exported from the module.
+// froms: an array of strings representing each of the imports.
// functions: an array of objects that represent all of the functions
// declared in the file.
// global: an object representing the global object. Its .context property
// is an object containing a property for each global variable.
// id: "(JSLint)"
-// imports: an array of strings representing each of the imports.
// json: true if the file is a JSON text.
// lines: an array of strings, the source.
// module: true if an import or export statement was used.
@@ -50,7 +53,7 @@
// tokens: an array of objects representing the tokens in the file.
// tree: the token objects arranged in a tree.
// warnings: an array of warning objects. A warning object can contain:
-// name: 'JSLintError'
+// name: "JSLintError"
// column: A column number in the file.
// line: A line number in the file.
// code: A warning code string.
@@ -76,203 +79,164 @@
// 5. Check the whitespace between the tokens.
// jslint can also examine JSON text. It decides that a file is JSON text if
-// the first token is '[' or '{'. Processing of JSON text is much simpler than
+// the first token is "[" or "{". Processing of JSON text is much simpler than
// the processing of JavaScript programs. Only the first three phases are
// required.
// WARNING: JSLint will hurt your feelings.
/*property
- a, and, arity, b, bad_assignment_a, bad_character_number_a,
- bad_directive_a, bad_get, bad_module_name_a, bad_option_a, bad_property_a,
- bad_set, bitwise, block, body, browser, c, calls, catch, charAt,
- charCodeAt, closer, closure, code, column, concat, constant, context,
- couch, create, d, dead, devel, directive, disrupt, dot, duplicate_a,
- edition, ellipsis, else, empty_block, es6, eval, every, expected_a_at_b_c,
+ a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get,
+ bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block,
+ body, browser, c, calls, catch, charCodeAt, closer, closure, code, column,
+ concat, constant, context, convert, couch, create, d, dead, default, devel,
+ directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else,
+ empty_block, escape_mega, eval, every, expected_a, expected_a_at_b_c,
expected_a_b, expected_a_b_from_c_d, expected_a_before_b,
- expected_digits_after_a, expected_four_digits, expected_identifier_a,
- expected_line_break_a_b, expected_regexp_factor_a, expected_space_a_b,
- expected_statements_a, expected_string_a, expected_type_string_a,
- expression, extra, flag, for, forEach, free, from, fud, fudge, function,
- function_in_loop, functions, g, global, i, id, identifier, import, imports,
+ expected_a_next_at_b, expected_digits_after_a, expected_four_digits,
+ expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a,
+ expected_space_a_b, expected_statements_a, expected_string_a,
+ expected_type_string_a, exports, expression, extra, finally, flag, for,
+ forEach, free, freeze, freeze_exports, from, froms, fud, fudge,
+ function_in_loop, functions, g, getset, global, i, id, identifier, import,
inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys,
- label, label_a, lbp, led, length, level, line, lines, live, loop, m,
- margin, match, maxerr, maxlen, message, misplaced_a, misplaced_directive_a,
- module, naked_block, name, names, nested_comment, new, node, not_label_a,
- nud, ok, open, option, out_of_scope_a, parameters, pop, property, push,
- qmark, quote, redefinition_a_b, replace, reserved_a, role, search,
- signature, slash_equal, slice, some, sort, split, statement, stop, strict,
- subscript_a, switch, test, this, thru, toString, todo_comment, tokens,
- too_long, too_many, tree, type, u, unclosed_comment, unclosed_mega,
- unclosed_string, undeclared_a, unexpected_a, unexpected_a_after_b,
- unexpected_at_top_level_a, unexpected_char_a, unexpected_comment,
- unexpected_directive_a, unexpected_expression_a, unexpected_label_a,
- unexpected_parens, unexpected_space_a_b,
- unexpected_statement_a, unexpected_trailing_space, unexpected_typeof_a,
- uninitialized_a, unreachable_a, unregistered_property_a, unsafe, unused_a,
- use_spaces, used, value, var_loop, var_switch, variable, warning, warnings,
+ label, label_a, lbp, led, length, level, line, lines, live, long, loop, m,
+ margin, match, message, misplaced_a, misplaced_directive_a, missing_browser,
+ missing_m, module, naked_block, name, names, nested_comment, new, node,
+ not_label_a, nr, nud, number_isNaN, ok, open, opening, option,
+ out_of_scope_a, parameters, parent, pop, property, push, quote,
+ redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search,
+ shebang, signature, single, slice, some, sort, split, startsWith, statement,
+ stop, subscript_a, switch, test, this, thru, toString, todo_comment,
+ tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment,
+ unclosed_mega, unclosed_string, undeclared_a, unexpected_a,
+ unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a,
+ unexpected_char_a, unexpected_comment, unexpected_directive_a,
+ unexpected_expression_a, unexpected_label_a, unexpected_parens,
+ unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space,
+ unexpected_typeof_a, uninitialized_a, unreachable_a,
+ unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces,
+ used, value, var_loop, var_switch, variable, warning, warnings,
weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white,
- wrap_assignment, wrap_condition, wrap_immediate, wrap_regexp, wrap_unary,
+ wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary,
wrapped, writable, y
*/
-var jslint = (function JSLint() {
- 'use strict';
-
- function empty() {
+function empty() {
// The empty function produces a new empty object that inherits nothing. This is
-// much better than {} because confusions around accidental method names like
+// much better than '{}' because confusions around accidental method names like
// 'constructor' are completely avoided.
- return Object.create(null);
- }
+ return Object.create(null);
+}
- function populate(object, array, value) {
+function populate(array, object = empty(), value = true) {
// Augment an object by taking property names from an array of strings.
- array.forEach(function (name) {
- object[name] = value;
- });
- }
+ array.forEach(function (name) {
+ object[name] = value;
+ });
+ return object;
+}
- var allowed_option = {
+const allowed_option = {
// These are the options that are recognized in the option object or that may
// appear in a /*jslint*/ directive. Most options will have a boolean value,
// usually true. Some options will also predefine some number of global
// variables.
- bitwise: true,
- browser: [
- 'Audio', 'clearInterval', 'clearTimeout', 'document', 'event',
- 'FormData', 'history', 'Image', 'localStorage', 'location', 'name',
- 'navigator', 'Option', 'screen', 'sessionStorage', 'setInterval',
- 'setTimeout', 'Storage', 'XMLHttpRequest'
- ],
- couch: [
- 'emit', 'getRow', 'isArray', 'log', 'provides', 'registerType',
- 'require', 'send', 'start', 'sum', 'toJSON'
- ],
- devel: [
- 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH'
- ],
- es6: [
- 'ArrayBuffer', 'DataView', 'Float32Array', 'Float64Array',
- 'Generator', 'GeneratorFunction', 'Int8Array', 'Int16Array',
- 'Int32Array', 'Intl', 'Map', 'Promise', 'Proxy', 'Reflect',
- 'Set', 'Symbol', 'System', 'Uint8Array', 'Uint8ClampedArray',
- 'Uint16Array', 'Uint32Array', 'WeakMap', 'WeakSet'
- ],
- eval: true,
- for: true,
- fudge: true,
- maxerr: 10000,
- maxlen: 10000,
- node: [
- 'Buffer', 'clearImmediate', 'clearInterval', 'clearTimeout',
- 'console', 'exports', 'global', 'module', 'process',
- 'require', 'setImmediate', 'setInterval', 'setTimeout',
- '__dirname', '__filename'
- ],
- this: true,
- white: true
- };
-
- var spaceop = {
-
-// This is the set of infix operators that require a space on each side.
-
- '!=': true,
- '!==': true,
- '%': true,
- '%=': true,
- '&': true,
- '&=': true,
- '&&': true,
- '*': true,
- '*=': true,
- '+=': true,
- '-=': true,
- '/': true,
- '/=': true,
- '<': true,
- '<=': true,
- '<<': true,
- '<<=': true,
- '=': true,
- '==': true,
- '===': true,
- '=>': true,
- '>': true,
- '>=': true,
- '>>': true,
- '>>=': true,
- '>>>': true,
- '>>>=': true,
- '^': true,
- '^=': true,
- '|': true,
- '|=': true,
- '||': true
- };
-
- var bitwiseop = {
+ bitwise: true,
+ browser: [
+ "caches", "clearInterval", "clearTimeout", "document", "DOMException",
+ "Element", "Event", "event", "FileReader", "FormData", "history",
+ "localStorage", "location", "MutationObserver", "name", "navigator",
+ "screen", "sessionStorage", "setInterval", "setTimeout", "Storage",
+ "TextDecoder", "TextEncoder", "URL", "window", "Worker",
+ "XMLHttpRequest"
+ ],
+ couch: [
+ "emit", "getRow", "isArray", "log", "provides", "registerType",
+ "require", "send", "start", "sum", "toJSON"
+ ],
+ convert: true,
+ devel: [
+ "alert", "confirm", "console", "prompt"
+ ],
+ eval: true,
+ for: true,
+ fudge: true,
+ getset: true,
+ long: true,
+ node: [
+ "Buffer", "clearImmediate", "clearInterval", "clearTimeout",
+ "console", "exports", "module", "process", "require",
+ "setImmediate", "setInterval", "setTimeout", "TextDecoder",
+ "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename"
+ ],
+ single: true,
+ this: true,
+ white: true
+};
+
+const anticondition = populate([
+ "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%",
+ "typeof", "(number)", "(string)"
+]);
// These are the bitwise operators.
- '~': true,
- '^': true,
- '^=': true,
- '&': true,
- '&=': true,
- '|': true,
- '|=': true,
- '<<': true,
- '<<=': true,
- '>>': true,
- '>>=': true,
- '>>>': true,
- '>>>=': true
- };
+const bitwiseop = populate([
+ "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=",
+ ">>>", ">>>="
+]);
- var opener = {
+const escapeable = populate([
+ "\\", "/", "`", "b", "f", "n", "r", "t"
+]);
-// The open and close pairs.
+const opener = {
- '(': ')', // paren
- '[': ']', // bracket
- '{': '}', // brace
- '${': '}' // mega
- };
+// The open and close pairs.
- var relationop = {
+ "(": ")", // paren
+ "[": "]", // bracket
+ "{": "}", // brace
+ "${": "}" // mega
+};
// The relational operators.
- '!=': true,
- '!==': true,
- '==': true,
- '===': true,
- '<': true,
- '<=': true,
- '>': true,
- '>=': true
- };
+const relationop = populate([
+ "!=", "!==", "==", "===", "<", "<=", ">", ">="
+]);
+
+// This is the set of infix operators that require a space on each side.
+
+const spaceop = populate([
+ "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/",
+ "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=",
+ ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
+]);
- var standard = [
+const standard = [
-// These are the globals that are provided by the ES5 language standard.
+// These are the globals that are provided by the language standard.
- 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent',
- 'encodeURI', 'encodeURIComponent', 'Error', 'EvalError', 'Function',
- 'isFinite', 'isNaN', 'JSON', 'Math', 'Number', 'Object', 'parseInt',
- 'parseFloat', 'RangeError', 'ReferenceError', 'RegExp', 'String',
- 'SyntaxError', 'TypeError', 'URIError'
- ];
+ "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI",
+ "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error",
+ "EvalError", "Float32Array", "Float64Array", "Generator",
+ "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl",
+ "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat",
+ "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp",
+ "Set", "String", "Symbol", "SyntaxError", "System", "TypeError",
+ "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array",
+ "URIError", "WeakMap", "WeakSet"
+];
- var bundle = {
+const bundle = {
// The bundle contains the raw text messages that are generated by jslint. It
// seems that they are all error messages and warnings. There are no "Atta
@@ -281,279 +245,312 @@ var jslint = (function JSLint() {
// wound the inner child. But if you accept it as sound advice rather than as
// personal criticism, it can make your programs better.
- and: "The '&&' subexpression should be wrapped in parens.",
- bad_assignment_a: "Bad assignment to '{a}'.",
- bad_character_number_a: "Bad character code: '{a}'",
- bad_directive_a: "Bad directive '{a}'.",
- bad_get: "A get function takes no parameters.",
- bad_module_name_a: "Bad module name '{a}'.",
- bad_option_a: "Bad option '{a}'.",
- bad_property_a: "Bad property name '{a}'.",
- bad_set: "A set function takes one parameter.",
- duplicate_a: "Duplicate '{a}'.",
- empty_block: "Empty block.",
- es6: "Unexpected ES6 feature.",
- expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
- expected_a_b: "Expected '{a}' and instead saw '{b}'.",
- expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.",
- expected_a_before_b: "Expected '{a}' before '{b}'.",
- expected_digits_after_a: "Expected digits after '{a}'.",
- expected_four_digits: "Expected four digits after '\\u'.",
- expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
- expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
- expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
- expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
- expected_statements_a: "Expected statements before '{a}'.",
- expected_string_a: "Expected a string and instead saw '{a}'.",
- expected_type_string_a: "Expected a type string and instead saw '{a}'.",
- function_in_loop: "Don't make functions within a loop.",
- infix_in: "Unexpected 'in'. Compare with undefined, or use the hasOwnProperty method instead.",
- isNaN: "Use the isNaN function to compare with NaN.",
- label_a: "'{a}' is a statement label.",
- misplaced_a: "Place '{a}' at the outermost level.",
- misplaced_directive_a: "Place the '/*{a}*/' directive before the first statement.",
- naked_block: "Naked block.",
- nested_comment: "Nested comment.",
- not_label_a: "'{a}' is not a label.",
- out_of_scope_a: "'{a}' is out of scope.",
- redefinition_a_b: "Redefinition of '{a}' from line {b}.",
- reserved_a: "Reserved name '{a}'.",
- slash_equal: "A regular expression literal can be confused with '/='.",
- subscript_a: "['{a}'] is better written in dot notation.",
- todo_comment: "Unexpected TODO comment.",
- too_long: "Line too long.",
- too_many: "Too many warnings.",
- unclosed_comment: "Unclosed comment.",
- unclosed_mega: "Unclosed mega literal.",
- unclosed_string: "Unclosed string.",
- undeclared_a: "Undeclared '{a}'.",
- unexpected_a: "Unexpected '{a}'.",
- unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
- unexpected_at_top_level_a: "Unexpected '{a}' at top level.",
- unexpected_char_a: "Unexpected character '{a}'.",
- unexpected_comment: "Unexpected comment.",
- unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
- unexpected_expression_a: "Unexpected expression '{a}' in statement position.",
- unexpected_label_a: "Unexpected label '{a}'.",
- unexpected_parens: "Don't wrap function literals in parens.",
- unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
- unexpected_statement_a: "Unexpected statement '{a}' in expression position.",
- unexpected_trailing_space: "Unexpected trailing space.",
- unexpected_typeof_a: "Unexpected 'typeof'. Use '===' to compare directly with {a}.",
- uninitialized_a: "Uninitialized '{a}'.",
- unreachable_a: "Unreachable '{a}'.",
- unregistered_property_a: "Unregistered property name '{a}'.",
- unsafe: "Unsafe character '{a}'.",
- unused_a: "Unused '{a}'.",
- use_spaces: "Use spaces, not tabs.",
- var_loop: "Don't declare variables in a loop.",
- var_switch: "Don't declare variables in a switch.",
- weird_condition_a: "Weird condition '{a}'.",
- weird_expression_a: "Weird expression '{a}'.",
- weird_loop: "Weird loop.",
- weird_relation_a: "Weird relation '{a}'.",
- wrap_assignment: "Don't wrap assignment statements in parens.",
- wrap_condition: "Wrap the condition in parens.",
- wrap_immediate: "Wrap an immediate function invocation in " +
- "parentheses to assist the reader in understanding that the " +
- "expression is the result of a function, and not the " +
- "function itself.",
- wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
- wrap_unary: "Wrap the unary expression in parens."
- };
+ and: "The '&&' subexpression should be wrapped in parens.",
+ bad_assignment_a: "Bad assignment to '{a}'.",
+ bad_directive_a: "Bad directive '{a}'.",
+ bad_get: "A get function takes no parameters.",
+ bad_module_name_a: "Bad module name '{a}'.",
+ bad_option_a: "Bad option '{a}'.",
+ bad_property_a: "Bad property name '{a}'.",
+ bad_set: "A set function takes one parameter.",
+ duplicate_a: "Duplicate '{a}'.",
+ empty_block: "Empty block.",
+ escape_mega: "Unexpected escapement in mega literal.",
+ expected_a: "Expected '{a}'.",
+ expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
+ expected_a_b: "Expected '{a}' and instead saw '{b}'.",
+ expected_a_b_from_c_d: (
+ "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'."
+ ),
+ expected_a_before_b: "Expected '{a}' before '{b}'.",
+ expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.",
+ expected_digits_after_a: "Expected digits after '{a}'.",
+ expected_four_digits: "Expected four digits after '\\u'.",
+ expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
+ expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
+ expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
+ expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
+ expected_statements_a: "Expected statements before '{a}'.",
+ expected_string_a: "Expected a string and instead saw '{a}'.",
+ expected_type_string_a: "Expected a type string and instead saw '{a}'.",
+ freeze_exports: (
+ "Expected 'Object.freeze('. All export values should be frozen."
+ ),
+ function_in_loop: "Don't make functions within a loop.",
+ infix_in: (
+ "Unexpected 'in'. Compare with undefined, "
+ + "or use the hasOwnProperty method instead."
+ ),
+ label_a: "'{a}' is a statement label.",
+ misplaced_a: "Place '{a}' at the outermost level.",
+ misplaced_directive_a: (
+ "Place the '/*{a}*/' directive before the first statement."
+ ),
+ missing_browser: "/*global*/ requires the Assume a browser option.",
+ missing_m: "Expected 'm' flag on a multiline regular expression.",
+ naked_block: "Naked block.",
+ nested_comment: "Nested comment.",
+ not_label_a: "'{a}' is not a label.",
+ number_isNaN: "Use Number.isNaN function to compare with NaN.",
+ out_of_scope_a: "'{a}' is out of scope.",
+ redefinition_a_b: "Redefinition of '{a}' from line {b}.",
+ required_a_optional_b: (
+ "Required parameter '{a}' after optional parameter '{b}'."
+ ),
+ reserved_a: "Reserved name '{a}'.",
+ subscript_a: "['{a}'] is better written in dot notation.",
+ todo_comment: "Unexpected TODO comment.",
+ too_long: "Line is longer than 80 characters.",
+ too_many_digits: "Too many digits.",
+ unclosed_comment: "Unclosed comment.",
+ unclosed_mega: "Unclosed mega literal.",
+ unclosed_string: "Unclosed string.",
+ undeclared_a: "Undeclared '{a}'.",
+ unexpected_a: "Unexpected '{a}'.",
+ unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
+ unexpected_a_before_b: "Unexpected '{a}' before '{b}'.",
+ unexpected_at_top_level_a: "Expected '{a}' to be in a function.",
+ unexpected_char_a: "Unexpected character '{a}'.",
+ unexpected_comment: "Unexpected comment.",
+ unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
+ unexpected_expression_a: (
+ "Unexpected expression '{a}' in statement position."
+ ),
+ unexpected_label_a: "Unexpected label '{a}'.",
+ unexpected_parens: "Don't wrap function literals in parens.",
+ unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
+ unexpected_statement_a: (
+ "Unexpected statement '{a}' in expression position."
+ ),
+ unexpected_trailing_space: "Unexpected trailing space.",
+ unexpected_typeof_a: (
+ "Unexpected 'typeof'. Use '===' to compare directly with {a}."
+ ),
+ uninitialized_a: "Uninitialized '{a}'.",
+ unreachable_a: "Unreachable '{a}'.",
+ unregistered_property_a: "Unregistered property name '{a}'.",
+ unsafe: "Unsafe character '{a}'.",
+ unused_a: "Unused '{a}'.",
+ use_double: "Use double quotes, not single quotes.",
+ use_open: (
+ "Wrap a ternary expression in parens, "
+ + "with a line break after the left paren."
+ ),
+ use_spaces: "Use spaces, not tabs.",
+ var_loop: "Don't declare variables in a loop.",
+ var_switch: "Don't declare variables in a switch.",
+ weird_condition_a: "Weird condition '{a}'.",
+ weird_expression_a: "Weird expression '{a}'.",
+ weird_loop: "Weird loop.",
+ weird_relation_a: "Weird relation '{a}'.",
+ wrap_condition: "Wrap the condition in parens.",
+ wrap_immediate: (
+ "Wrap an immediate function invocation in parentheses to assist "
+ + "the reader in understanding that the expression is the result "
+ + "of a function, and not the function itself."
+ ),
+ wrap_parameter: "Wrap the parameter in parens.",
+ wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
+ wrap_unary: "Wrap the unary expression in parens."
+};
// Regular expression literals:
// supplant {variables}
- var rx_supplant = /\{([^{}]*)\}/g,
+const rx_supplant = /\{([^{}]*)\}/g;
// carriage return, carriage return linefeed, or linefeed
- rx_crlf = /\n|\r\n?/,
+const rx_crlf = /\n|\r\n?/;
// unsafe characters that are silently deleted by one or more browsers
- rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
+const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
// identifier
- rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
- rx_bad_property = /^_|\$|Sync$|_$/,
+const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
+const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/;
+const rx_bad_property = /^_|\$|Sync\$|_$/;
// star slash
- rx_star_slash = /\*\//,
+const rx_star_slash = /\*\//;
// slash star
- rx_slash_star = /\/\*/,
+const rx_slash_star = /\/\*/;
// slash star or ending slash
- rx_slash_star_or_slash = /\/\*|\/$/,
+const rx_slash_star_or_slash = /\/\*|\/$/;
// uncompleted work comment
- rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/,
+const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/;
// tab
- rx_tab = /\t/g,
+const rx_tab = /\t/g;
// directive
- rx_directive = /^(jslint|property|global)\s*(.*)$/,
- rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)\s*(?::\s*(true|false|[0-9]+)\s*)?(?:,\s*)?(.*)$/,
+const rx_directive = /^(jslint|property|global)\s+(.*)$/;
+const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/;
// token (sorry it is so long)
- rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\]\?,:;'"~`]|=(?:==?|>)?|\.+|\/[=*\/]?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[\|=]?|>{1,3}=?|<=?|!={0,2}|(0|[1-9][0-9]*))(.*)$/,
- rx_digits = /^([0-9]+)(.*)$/,
- rx_hexs = /^([0-9a-fA-F]+)(.*)$/,
- rx_octals = /^([0-7]+)(.*)$/,
- rx_bits = /^([01]+)(.*)$/,
+const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<=?|!(?:!|==?)?|(0|[1-9][0-9]*))(.*)$/;
+const rx_digits = /^([0-9]+)(.*)$/;
+const rx_hexs = /^([0-9a-fA-F]+)(.*)$/;
+const rx_octals = /^([0-7]+)(.*)$/;
+const rx_bits = /^([01]+)(.*)$/;
// mega
- rx_mega = /`|\$\{/,
-// indentation
- rx_colons = /^(.*)\?([:.]*)$/,
- rx_dot = /\.$/,
+const rx_mega = /[`\\]|\$\{/;
// JSON number
- rx_JSON_number = /^-?\d+(?:\.\d*)?(?:e[\-+]?\d+)?$/i;
-
- function is_letter(string) {
- return (string >= 'a' && string <= 'z\uffff') ||
- (string >= 'A' && string <= 'Z\uffff');
- }
-
- function supplant(string, object) {
- return string.replace(rx_supplant, function (found, filling) {
- var replacement = object[filling];
- return (replacement !== undefined)
- ? replacement
- : found;
- });
- }
-
- var anon = "anonymous", // The guessed name for anonymous functions.
- blockage, // The current block.
- block_stack, // The stack of blocks.
- declared_globals, // The object containing the global declarations.
- directive_mode, // true if directives are still allowed.
- early_stop, // true if JSLint cannot finish.
- export_mode, // true if an export statement was seen.
- fudge, // true if the natural numbers start with 1.
- functionage, // The current function.
- functions, // The array containing all of the functions.
- global, // The global object, the outermost context.
- imports, // The array collecting all import-from strings.
- json_mode, // true if parsing JSON.
- lines, // The array containing source lines.
- module_mode, // true if import or export was used.
- next_token, // The next token to be examined in the parse.
- option, // The options parameter.
- property, // The object containing the tallied property names.
- mega_mode, // true if currently parsing a megastring literal.
- stack, // The stack of functions.
- syntax, // The object containing the parser.
- token, // The current token being examined in the parse.
- token_nr, // The number of the next token.
- tokens, // The array of tokens.
- tenure, // The predefined property registry.
- tree, // The abstract parse tree.
- var_mode, // true if using var, false if using let.
- warnings; // The array collecting all generated warnings.
+const rx_JSON_number = /^-?\d+(?:\.\d*)?(?:e[\-+]?\d+)?$/i;
+// initial cap
+const rx_cap = /^[A-Z]/;
+
+function is_letter(string) {
+ return (
+ (string >= "a" && string <= "z\uffff")
+ || (string >= "A" && string <= "Z\uffff")
+ );
+}
+
+function supplant(string, object) {
+ return string.replace(rx_supplant, function (found, filling) {
+ const replacement = object[filling];
+ return (
+ replacement !== undefined
+ ? replacement
+ : found
+ );
+ });
+}
+
+let anon; // The guessed name for anonymous functions.
+let blockage; // The current block.
+let block_stack; // The stack of blocks.
+let declared_globals; // The object containing the global declarations.
+let directives; // The directive comments.
+let directive_mode; // true if directives are still allowed.
+let early_stop; // true if JSLint cannot finish.
+let exports; // The exported names and values.
+let froms; // The array collecting all import-from strings.
+let fudge; // true if the natural numbers start with 1.
+let functionage; // The current function.
+let functions; // The array containing all of the functions.
+let global; // The global object; the outermost context.
+let json_mode; // true if parsing JSON.
+let lines; // The array containing source lines.
+let mega_mode; // true if currently parsing a megastring literal.
+let module_mode; // true if import or export was used.
+let next_token; // The next token to be examined in the parse.
+let option; // The options parameter.
+let property; // The object containing the tallied property names.
+let shebang; // true if a #! was seen on the first line.
+let stack; // The stack of functions.
+let syntax; // The object containing the parser.
+let token; // The current token being examined in the parse.
+let token_nr; // The number of the next token.
+let tokens; // The array of tokens.
+let tenure; // The predefined property registry.
+let tree; // The abstract parse tree.
+let var_mode; // "var" if using var; "let" if using let.
+let warnings; // The array collecting all generated warnings.
// Error reportage functions:
- function artifact(the_token) {
+function artifact(the_token) {
// Return a string representing an artifact.
- if (the_token === undefined) {
- the_token = next_token;
- }
- return (the_token.id === '(string)' || the_token.id === '(number)')
- ? String(the_token.value)
- : the_token.id;
+ if (the_token === undefined) {
+ the_token = next_token;
}
+ return (
+ (the_token.id === "(string)" || the_token.id === "(number)")
+ ? String(the_token.value)
+ : the_token.id
+ );
+}
- function artifact_line(the_token) {
+function artifact_line(the_token) {
// Return the fudged line number of an artifact.
- if (the_token === undefined) {
- the_token = next_token;
- }
- return the_token.line + fudge;
+ if (the_token === undefined) {
+ the_token = next_token;
}
+ return the_token.line + fudge;
+}
- function artifact_column(the_token) {
+function artifact_column(the_token) {
// Return the fudged column number of an artifact.
- if (the_token === undefined) {
- the_token = next_token;
- }
- return the_token.from + fudge;
+ if (the_token === undefined) {
+ the_token = next_token;
}
+ return the_token.from + fudge;
+}
- function warn_at(code, line, column, a, b, c, d) {
+function warn_at(code, line, column, a, b, c, d) {
// Report an error at some line and column of the program. The warning object
// resembles an exception.
- var warning = { // ~~
- name: 'JSLintError',
- column: column,
- line: line,
- code: code
- };
- if (a !== undefined) {
- warning.a = a;
- }
- if (b !== undefined) {
- warning.b = b;
- }
- if (c !== undefined) {
- warning.c = c;
- }
- if (d !== undefined) {
- warning.d = d;
- }
- warning.message = supplant(bundle[code] || code, warning);
- warnings.push(warning);
- return (typeof option.maxerr === 'number' &&
- warnings.length === option.maxerr)
- ? stop_at('too_many', line, column)
- : warning;
+ const warning = { // ~~
+ name: "JSLintError",
+ column,
+ line,
+ code
+ };
+ if (a !== undefined) {
+ warning.a = a;
+ }
+ if (b !== undefined) {
+ warning.b = b;
}
+ if (c !== undefined) {
+ warning.c = c;
+ }
+ if (d !== undefined) {
+ warning.d = d;
+ }
+ warning.message = supplant(bundle[code] || code, warning);
+ warnings.push(warning);
+ return warning;
+}
- function stop_at(code, line, column, a, b, c, d) {
+function stop_at(code, line, column, a, b, c, d) {
// Same as warn_at, except that it stops the analysis.
- throw warn_at(code, line, column, a, b, c, d);
- }
+ throw warn_at(code, line, column, a, b, c, d);
+}
- function warn(code, the_token, a, b, c, d) {
+function warn(code, the_token, a, b, c, d) {
// Same as warn_at, except the warning will be associated with a specific token.
// If there is already a warning on this token, suppress the new one. It is
// likely that the first warning will be the most meaningful.
- if (the_token === undefined) {
- the_token = next_token;
- }
- if (the_token.warning === undefined) {
- the_token.warning = warn_at(
- code,
- the_token.line,
- the_token.from,
- a || artifact(the_token),
- b,
- c,
- d
- );
- return the_token.warning;
- }
+ if (the_token === undefined) {
+ the_token = next_token;
+ }
+ if (the_token.warning === undefined) {
+ the_token.warning = warn_at(
+ code,
+ the_token.line,
+ the_token.from,
+ a || artifact(the_token),
+ b,
+ c,
+ d
+ );
+ return the_token.warning;
}
+}
- function stop(code, the_token, a, b, c, d) {
+function stop(code, the_token, a, b, c, d) {
// Similar to warn and stop_at. If the token already had a warning, that
// warning will be replaced with this new one. It is likely that the stopping
-// warning will be the most meaningful.
+// warning will be the more meaningful.
- if (the_token === undefined) {
- the_token = next_token;
- }
- the_token.warning = undefined;
- throw warn(code, the_token, a, b, c, d);
+ if (the_token === undefined) {
+ the_token = next_token;
}
+ delete the_token.warning;
+ throw warn(code, the_token, a, b, c, d);
+}
// Tokenize:
- function tokenize(source) {
+function tokenize(source) {
// tokenize takes a source and produces from it an array of token objects.
// JavaScript is notoriously difficult to tokenize because of the horrible
@@ -565,61 +562,81 @@ var jslint = (function JSLint() {
// If the source is not an array, then it is split into lines at the
// carriage return/linefeed.
- lines = (Array.isArray(source))
- ? source
- : source.split(rx_crlf);
- tokens = [];
-
- var char, // a popular character
- column = 0, // the column number of the next character
- from, // the starting column number of the token
- line = -1, // the line number of the next character
- previous = global, // the previous token including comments
- prior = global, // the previous token excluding comments
- mega_from, // the starting column of megastring
- mega_line, // the starting line of megastring
- snippet, // a piece of string
- source_line; // the current line source string
+ lines = (
+ Array.isArray(source)
+ ? source
+ : source.split(rx_crlf)
+ );
+ tokens = [];
+
+ let char; // a popular character
+ let column = 0; // the column number of the next character
+ let first; // the first token
+ let from; // the starting column number of the token
+ let line = -1; // the line number of the next character
+ let nr = 0; // the next token number
+ let previous = global; // the previous token including comments
+ let prior = global; // the previous token excluding comments
+ let mega_from; // the starting column of megastring
+ let mega_line; // the starting line of megastring
+ let regexp_seen; // regular expression literal seen on this line
+ let snippet; // a piece of string
+ let source_line = ""; // the remaining line source string
+ let whole_line = ""; // the whole line source string
+
+ if (lines[0].startsWith("#!")) {
+ line = 0;
+ shebang = true;
+ }
- function next_line() {
+ function next_line() {
// Put the next line of source in source_line. If the line contains tabs,
// replace them with spaces and give a warning. Also warn if the line contains
// unsafe characters or is too damn long.
- var at;
- column = 0;
- line += 1;
- source_line = lines[line];
- if (source_line !== undefined) {
- at = source_line.search(rx_tab);
- if (at >= 0) {
- if (!option.white) {
- warn_at('use_spaces', line, at + 1);
- }
- source_line = source_line.replace(rx_tab, ' ');
- }
- at = source_line.search(rx_unsafe);
- if (at >= 0) {
- warn_at(
- 'unsafe',
- line,
- column + at,
- 'U+' + source_line.charCodeAt(at).toString(16)
- );
- }
- if (option.maxlen && option.maxlen < source_line.length) {
- warn_at('too_long', line, source_line.length);
- } else if (!option.white && source_line.slice(-1) === ' ') {
- warn_at(
- 'unexpected_trailing_space',
- line,
- source_line.length - 1
- );
+ let at;
+ if (
+ !option.long
+ && whole_line.length > 80
+ && !json_mode
+ && first
+ && !regexp_seen
+ ) {
+ warn_at("too_long", line, 80);
+ }
+ column = 0;
+ line += 1;
+ regexp_seen = false;
+ source_line = lines[line];
+ whole_line = source_line || "";
+ if (source_line !== undefined) {
+ at = source_line.search(rx_tab);
+ if (at >= 0) {
+ if (!option.white) {
+ warn_at("use_spaces", line, at + 1);
}
+ source_line = source_line.replace(rx_tab, " ");
+ }
+ at = source_line.search(rx_unsafe);
+ if (at >= 0) {
+ warn_at(
+ "unsafe",
+ line,
+ column + at,
+ "U+" + source_line.charCodeAt(at).toString(16)
+ );
+ }
+ if (!option.white && source_line.slice(-1) === " ") {
+ warn_at(
+ "unexpected_trailing_space",
+ line,
+ source_line.length - 1
+ );
}
- return source_line;
}
+ return source_line;
+ }
// Most tokens, including the identifiers, operators, and punctuators, can be
// found with a regular expression. Regular expressions cannot correctly match
@@ -628,607 +645,621 @@ var jslint = (function JSLint() {
// don't provide good warnings. The functions snip, next_char, prev_char,
// some_digits, and escape help in the parsing of literals.
- function snip() {
+ function snip() {
// Remove the last character from snippet.
- snippet = snippet.slice(0, -1);
- }
+ snippet = snippet.slice(0, -1);
+ }
- function next_char(match) {
+ function next_char(match) {
// Get the next character from the source line. Remove it from the source_line,
// and append it to the snippet. Optionally check that the previous character
// matched an expected value.
- if (match !== undefined && char !== match) {
- return stop_at('expected_a_b', line, column, match, char);
- }
- if (source_line) {
- char = source_line.charAt(0);
- source_line = source_line.slice(1);
- snippet += char;
- } else {
- char = '';
- snippet += ' ';
- }
- column += 1;
- return char;
+ if (match !== undefined && char !== match) {
+ return stop_at(
+ (
+ char === ""
+ ? "expected_a"
+ : "expected_a_b"
+ ),
+ line,
+ column - 1,
+ match,
+ char
+ );
+ }
+ if (source_line) {
+ char = source_line[0];
+ source_line = source_line.slice(1);
+ snippet += char;
+ } else {
+ char = "";
+ snippet += " ";
}
+ column += 1;
+ return char;
+ }
- function back_char() {
+ function back_char() {
// Back up one character by moving a character from the end of the snippet to
// the front of the source_line.
- if (snippet) {
- char = snippet.slice(-1);
- source_line = char + source_line;
- column -= 1;
- snip();
- } else {
- char = '';
- }
- return char;
+ if (snippet) {
+ char = snippet.slice(-1);
+ source_line = char + source_line;
+ column -= 1;
+ snip();
+ } else {
+ char = "";
}
+ return char;
+ }
- function some_digits(rx, quiet) {
- var result = source_line.match(rx);
- if (result) {
- char = result[1];
- column += char.length;
- source_line = result[2];
- snippet += char;
- } else {
- char = '';
- if (!quiet) {
- warn_at(
- 'expected_digits_after_a',
- line,
- column,
- snippet
- );
- }
+ function some_digits(rx, quiet) {
+ const result = source_line.match(rx);
+ if (result) {
+ char = result[1];
+ column += char.length;
+ source_line = result[2];
+ snippet += char;
+ } else {
+ char = "";
+ if (!quiet) {
+ warn_at(
+ "expected_digits_after_a",
+ line,
+ column,
+ snippet
+ );
}
- return char.length;
- }
-
- function escape(extra) {
- switch (next_char('\\')) {
- case '\\':
- case '\'':
- case '"':
- case '/':
- case ':':
- case '=':
- case '|':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- case ' ':
- break;
- case 'u':
- if (next_char('u') === '{') {
- if (some_digits(rx_hexs) > 5) {
- warn_at('too_many_digits', line, column - 1);
- }
- if (!option.es6) {
- warn_at('es6', line, column);
- }
- if (next_char() !== '}') {
- stop_at('expected_a_before_b', line, column, '}', char);
- }
- return;
+ }
+ return char.length;
+ }
+
+ function escape(extra) {
+ next_char("\\");
+ if (escapeable[char] === true) {
+ return next_char();
+ }
+ if (char === "") {
+ return stop_at("unclosed_string", line, column);
+ }
+ if (char === "u") {
+ if (next_char("u") === "{") {
+ if (json_mode) {
+ warn_at("unexpected_a", line, column - 1, char);
}
- back_char();
- if (some_digits(rx_hexs, true) < 4) {
- warn_at('expected_four_digits', line, column - 1);
+ if (some_digits(rx_hexs) > 5) {
+ warn_at("too_many_digits", line, column - 1);
}
- break;
- case '':
- return stop_at('unclosed_string', line, column);
- default:
- if (extra && extra.indexOf(char) < 0) {
- warn_at('unexpected_a_after_b', line, column, char, '\\');
+ if (next_char() !== "}") {
+ stop_at("expected_a_before_b", line, column, "}", char);
}
+ return next_char();
}
- next_char();
+ back_char();
+ if (some_digits(rx_hexs, true) < 4) {
+ warn_at("expected_four_digits", line, column - 1);
+ }
+ return;
+ }
+ if (extra && extra.indexOf(char) >= 0) {
+ return next_char();
}
+ warn_at("unexpected_a_before_b", line, column - 2, "\\", char);
+ }
- function make(id, value, identifier) {
+ function make(id, value, identifier) {
// Make the token object and append it to the tokens list.
- var the_token = {
- id: id,
- identifier: !!identifier,
- from: from,
- thru: column,
- line: line
- };
- tokens.push(the_token);
+ const the_token = {
+ from,
+ id,
+ identifier: Boolean(identifier),
+ line,
+ nr,
+ thru: column
+ };
+ tokens[nr] = the_token;
+ nr += 1;
// Directives must appear before the first statement.
- if (id !== '(comment)') {
- directive_mode = false;
- }
+ if (id !== "(comment)" && id !== ";") {
+ directive_mode = false;
+ }
// If the token is to have a value, give it one.
- if (value !== undefined) {
- the_token.value = value;
- }
+ if (value !== undefined) {
+ the_token.value = value;
+ }
// If this token is an identifier that touches a preceding number, or
-// a '/', comment, or regular expression literal that touches a preceding
+// a "/", comment, or regular expression literal that touches a preceding
// comment or regular expression literal, then give a missing space warning.
// This warning is not suppressed by option.white.
- if (
- previous.line === line &&
- previous.thru === from &&
- (
- (id === '(comment)' || id === '(regexp)' || id === '/') &&
- (
- previous.id === '(comment)' ||
- previous.id === '(regexp)'
- )
- )
- ) {
- warn(
- 'expected_space_a_b',
- the_token,
- artifact(previous),
- artifact(the_token)
- );
- }
- if (previous.id === '.' && id === '(number)') {
- warn('expected_a_before_b', previous, '0', '.');
- }
- if (prior.id === '.' && the_token.identifier) {
- the_token.dot = true;
- }
+ if (
+ previous.line === line
+ && previous.thru === from
+ && (id === "(comment)" || id === "(regexp)" || id === "/")
+ && (previous.id === "(comment)" || previous.id === "(regexp)")
+ ) {
+ warn(
+ "expected_space_a_b",
+ the_token,
+ artifact(previous),
+ artifact(the_token)
+ );
+ }
+ if (previous.id === "." && id === "(number)") {
+ warn("expected_a_before_b", previous, "0", ".");
+ }
+ if (prior.id === "." && the_token.identifier) {
+ the_token.dot = true;
+ }
// The previous token is used to detect adjacency problems.
- previous = the_token;
+ previous = the_token;
// The prior token is a previous token that was not a comment. The prior token
-// is used to disambiguate '/', which can mean division or regular expression
+// is used to disambiguate "/", which can mean division or regular expression
// literal.
- if (previous.id !== '(comment)') {
- prior = previous;
- }
- return the_token;
+ if (previous.id !== "(comment)") {
+ prior = previous;
}
+ return the_token;
+ }
- function directive(the_comment, body) {
+ function parse_directive(the_comment, body) {
// JSLint recognizes three directives that can be encoded in comments. This
// function processes one item, and calls itself recursively to process the
// next one.
- var result = body.match(rx_directive_part);
- if (result) {
- var allowed,
- name = result[1],
- value = result[2];
- switch (the_comment.directive) {
- case 'jslint':
- allowed = allowed_option[name];
- switch (typeof allowed) {
- case 'boolean':
- case 'object':
- switch (value) {
- case 'true':
- case '':
- case undefined:
- option[name] = true;
- if (Array.isArray(allowed)) {
- populate(declared_globals, allowed, false);
- }
- break;
- case 'false':
- option[name] = false;
- break;
- default:
- warn('bad_option_a', the_comment, name + ':' + value);
- }
- break;
- case 'number':
- if (isFinite(+value)) {
- option[name] = +value;
- } else {
- warn('bad_option_a', the_comment, name + ':' + value);
+ const result = body.match(rx_directive_part);
+ if (result) {
+ let allowed;
+ const name = result[1];
+ const value = result[2];
+ if (the_comment.directive === "jslint") {
+ allowed = allowed_option[name];
+ if (
+ typeof allowed === "boolean"
+ || typeof allowed === "object"
+ ) {
+ if (
+ value === ""
+ || value === "true"
+ || value === undefined
+ ) {
+ option[name] = true;
+ if (Array.isArray(allowed)) {
+ populate(allowed, declared_globals, false);
}
- break;
- default:
- warn('bad_option_a', the_comment, name);
- }
- break;
- case 'property':
- if (tenure === undefined) {
- tenure = empty();
- }
- tenure[name] = true;
- break;
- case 'global':
- if (value) {
- warn('bad_option_a', the_comment, name + ':' + value);
+ } else if (value === "false") {
+ option[name] = false;
+ } else {
+ warn("bad_option_a", the_comment, name + ":" + value);
}
- declared_globals[name] = false;
- module_mode = the_comment;
- break;
+ } else {
+ warn("bad_option_a", the_comment, name);
}
- return directive(the_comment, result[3]);
- }
- if (body) {
- return stop('bad_directive_a', the_comment, body);
+ } else if (the_comment.directive === "property") {
+ if (tenure === undefined) {
+ tenure = empty();
+ }
+ tenure[name] = true;
+ } else if (the_comment.directive === "global") {
+ if (value) {
+ warn("bad_option_a", the_comment, name + ":" + value);
+ }
+ declared_globals[name] = false;
+ module_mode = the_comment;
}
+ return parse_directive(the_comment, result[3]);
+ }
+ if (body) {
+ return stop("bad_directive_a", the_comment, body);
}
+ }
- function comment(snippet) {
+ function comment(snippet) {
// Make a comment object. Comments are not allowed in JSON text. Comments can
// include directives and notices of incompletion.
- var the_comment = make('(comment)', snippet);
- if (Array.isArray(snippet)) {
- snippet = snippet.join(' ');
- }
- if (!option.devel && rx_todo.test(snippet)) {
- warn('todo_comment', the_comment);
- }
- var result = snippet.match(rx_directive);
- if (result) {
- if (!directive_mode) {
- warn_at('misplaced_directive_a', line, from, result[1]);
- } else {
- the_comment.directive = result[1];
- directive(the_comment, result[2]);
- }
+ const the_comment = make("(comment)", snippet);
+ if (Array.isArray(snippet)) {
+ snippet = snippet.join(" ");
+ }
+ if (!option.devel && rx_todo.test(snippet)) {
+ warn("todo_comment", the_comment);
+ }
+ const result = snippet.match(rx_directive);
+ if (result) {
+ if (!directive_mode) {
+ warn_at("misplaced_directive_a", line, from, result[1]);
+ } else {
+ the_comment.directive = result[1];
+ parse_directive(the_comment, result[2]);
}
- return the_comment;
+ directives.push(the_comment);
}
+ return the_comment;
+ }
- function regexp() {
+ function regexp() {
// Parse a regular expression literal.
- var result,
- u_mode = false,
- value;
+ let multi_mode = false;
+ let result;
+ let value;
+ regexp_seen = true;
- function quantifier() {
+ function quantifier() {
// Match an optional quantifier.
- switch (char) {
- case '?':
- case '*':
- case '+':
- next_char();
- break;
- case '{':
- if (some_digits(rx_digits, true) === 0) {
- warn_at('expected_a', line, column, '0');
- }
- if (next_char() === ',') {
- some_digits(rx_digits, true);
- next_char();
- }
- next_char('}');
- break;
- default:
- return;
+ if (char === "?" || char === "*" || char === "+") {
+ next_char();
+ } else if (char === "{") {
+ if (some_digits(rx_digits, true) === 0) {
+ warn_at("expected_a", line, column, "0");
}
- if (char === '?') {
- next_char('?');
+ if (next_char() === ",") {
+ some_digits(rx_digits, true);
+ next_char();
}
+ next_char("}");
+ } else {
+ return;
}
+ if (char === "?") {
+ next_char("?");
+ }
+ }
- function subklass() {
+ function subklass() {
// Match a character in a character class.
- switch (char) {
- case '\\':
- escape();
- return true;
- case '[':
- case ']':
- case '/':
- case '^':
- case '-':
- case '|':
- case '':
- return false;
- case '`':
- if (mega_mode) {
- warn_at('unexpected_a', line, column, '`');
- }
- next_char();
- return true;
- case ' ':
- warn_at('expected_a_before_b', line, column, '\\', ' ');
- next_char();
- return true;
- default:
- next_char();
- return true;
- }
+ if (char === "\\") {
+ escape("BbDdSsWw-[]^");
+ return true;
+ }
+ if (
+ char === ""
+ || char === "["
+ || char === "]"
+ || char === "/"
+ || char === "^"
+ || char === "-"
+ ) {
+ return false;
}
+ if (char === " ") {
+ warn_at("expected_a_b", line, column, "\\u0020", " ");
+ } else if (char === "`" && mega_mode) {
+ warn_at("unexpected_a", line, column, "`");
+ }
+ next_char();
+ return true;
+ }
- function range() {
+ function ranges() {
// Match a range of subclasses.
- if (subklass()) {
- if (char === '-') {
- next_char('-');
- if (!subklass()) {
- return stop_at('unexpected_a', line, column - 1, '-');
- }
+ if (subklass()) {
+ if (char === "-") {
+ next_char("-");
+ if (!subklass()) {
+ return stop_at(
+ "unexpected_a",
+ line,
+ column - 1,
+ "-"
+ );
}
- return range();
}
+ return ranges();
}
+ }
- function klass() {
+ function klass() {
// Match a class.
- next_char('[');
- if (char === '^') {
- next_char('^');
- }
- range();
- next_char(']');
+ next_char("[");
+ if (char === "^") {
+ next_char("^");
}
+ (function classy() {
+ ranges();
+ if (char !== "]" && char !== "") {
+ warn_at(
+ "expected_a_before_b",
+ line,
+ column,
+ "\\",
+ char
+ );
+ next_char();
+ return classy();
+ }
+ }());
+ next_char("]");
+ }
- function choice() {
+ function choice() {
- function group() {
+ function group() {
// Match a group that starts with left paren.
- next_char('(');
- if (char === '?') {
- next_char('?');
- switch (char) {
- case ':':
- case '=':
- case '!':
- next_char();
- break;
- default:
- next_char(':');
- }
- } else if (char === ':') {
- warn_at('expected_a_before_b', line, column, '?', ':');
+ next_char("(");
+ if (char === "?") {
+ next_char("?");
+ if (char === "=" || char === "!") {
+ next_char();
+ } else {
+ next_char(":");
}
- choice();
- next_char(')');
+ } else if (char === ":") {
+ warn_at("expected_a_before_b", line, column, "?", ":");
}
+ choice();
+ next_char(")");
+ }
- function factor() {
- switch (char) {
- case '[':
- klass();
- return true;
- case '\\':
- escape('BbDdSsWw^${}[]().|*+?');
- return true;
- case '(':
- group();
- return true;
- case '/':
- case '|':
- case ']':
- case ')':
- case '}':
- case '{':
- case '?':
- case '+':
- case '*':
- case '':
- return false;
- case '`':
- if (mega_mode) {
- warn_at('unexpected_a', line, column, '`');
- }
- break;
- case ' ':
- warn_at('expected_a_before_b', line, column, '\\', ' ');
- break;
- }
- next_char();
+ function factor() {
+ if (
+ char === ""
+ || char === "/"
+ || char === "]"
+ || char === ")"
+ ) {
+ return false;
+ }
+ if (char === "(") {
+ group();
return true;
}
-
- function sequence(follow) {
- if (factor()) {
- quantifier();
- return sequence(true);
+ if (char === "[") {
+ klass();
+ return true;
+ }
+ if (char === "\\") {
+ escape("BbDdSsWw^${}[]():=!.-|*+?");
+ return true;
+ }
+ if (
+ char === "?"
+ || char === "+"
+ || char === "*"
+ || char === "}"
+ || char === "{"
+ ) {
+ warn_at(
+ "expected_a_before_b",
+ line,
+ column - 1,
+ "\\",
+ char
+ );
+ } else if (char === "`") {
+ if (mega_mode) {
+ warn_at("unexpected_a", line, column - 1, "`");
+ }
+ } else if (char === " ") {
+ warn_at(
+ "expected_a_b",
+ line,
+ column - 1,
+ "\\s",
+ " "
+ );
+ } else if (char === "$") {
+ if (source_line[0] !== "/") {
+ multi_mode = true;
}
- if (!follow) {
- warn_at('expected_regexp_factor_a', line, column, char);
+ } else if (char === "^") {
+ if (snippet !== "^") {
+ multi_mode = true;
}
}
+ next_char();
+ return true;
+ }
+
+ function sequence(follow) {
+ if (factor()) {
+ quantifier();
+ return sequence(true);
+ }
+ if (!follow) {
+ warn_at("expected_regexp_factor_a", line, column, char);
+ }
+ }
// Match a choice (a sequence that can be followed by | and another choice).
- sequence();
- if (char === '|') {
- next_char('|');
- return choice();
- }
+ sequence();
+ if (char === "|") {
+ next_char("|");
+ return choice();
}
+ }
// Scan the regexp literal. Give a warning if the first character is = because
// /= looks like a division assignment operator.
- snippet = '';
- next_char();
- if (char === '=') {
- warn_at('expected_a_before_b', line, column, '\\', '=');
- }
- choice();
+ snippet = "";
+ next_char();
+ if (char === "=") {
+ warn_at("expected_a_before_b", line, column, "\\", "=");
+ }
+ choice();
// Make sure there is a closing slash.
- snip();
- value = snippet;
- next_char('/');
+ snip();
+ value = snippet;
+ next_char("/");
// Process dangling flag letters.
- var allowed = {
- g: true,
- i: true,
- m: true,
- u: u_mode,
- y: option.es6
- },
- flag = empty();
- (function make_flag() {
- if (is_letter(char)) {
- if (allowed[char] !== true) {
- warn_at('unexpected_a', line, column, char);
- }
- allowed[char] = false;
- flag[char] = true;
- next_char();
- return make_flag();
+ const allowed = {
+ g: true,
+ i: true,
+ m: true,
+ u: true,
+ y: true
+ };
+ const flag = empty();
+ (function make_flag() {
+ if (is_letter(char)) {
+ if (allowed[char] !== true) {
+ warn_at("unexpected_a", line, column, char);
}
- }());
- if (u_mode && !flag.u) {
- warn_at('expected_a_before_b', line, column, 'u', char);
- }
- back_char();
- if (char === '/' || char === '*') {
- return stop_at('unexpected_a', line, from, char);
+ allowed[char] = false;
+ flag[char] = true;
+ next_char();
+ return make_flag();
}
- result = make('(regexp)', char);
- result.flag = flag;
- result.value = value;
- return result;
+ }());
+ back_char();
+ if (char === "/" || char === "*") {
+ return stop_at("unexpected_a", line, from, char);
}
+ result = make("(regexp)", char);
+ result.flag = flag;
+ result.value = value;
+ if (multi_mode && !flag.m) {
+ warn_at("missing_m", line, column);
+ }
+ return result;
+ }
- function string(quote) {
+ function string(quote) {
// Make a string token.
- var the_token;
- snippet = '';
- next_char();
-
- return (function next() {
- switch (char) {
- case quote:
- snip();
- the_token = make('(string)', snippet);
- the_token.quote = quote;
- return the_token;
- case '\\':
- escape();
- break;
- case '':
- return stop_at('unclosed_string', line, column);
- case '`':
- if (mega_mode) {
- warn_at('unexpected_a', line, column, '`');
- }
- next_char('`');
- break;
- default:
- next_char();
- }
- return next();
- }());
- }
+ let the_token;
+ snippet = "";
+ next_char();
- function frack() {
- if (char === '.') {
- some_digits(rx_digits);
- next_char();
+ return (function next() {
+ if (char === quote) {
+ snip();
+ the_token = make("(string)", snippet);
+ the_token.quote = quote;
+ return the_token;
}
- if (char === 'E' || char === 'e') {
- next_char();
- if (char !== '+' && char !== '-') {
- back_char();
+ if (char === "") {
+ return stop_at("unclosed_string", line, column);
+ }
+ if (char === "\\") {
+ escape(quote);
+ } else if (char === "`") {
+ if (mega_mode) {
+ warn_at("unexpected_a", line, column, "`");
}
- some_digits(rx_digits);
+ next_char("`");
+ } else {
next_char();
}
+ return next();
+ }());
+ }
+
+ function frack() {
+ if (char === ".") {
+ some_digits(rx_digits);
+ next_char();
+ }
+ if (char === "E" || char === "e") {
+ next_char();
+ if (char !== "+" && char !== "-") {
+ back_char();
+ }
+ some_digits(rx_digits);
+ next_char();
}
+ }
- function number() {
- if (snippet === '0') {
- switch (next_char()) {
- case '.':
- frack();
- break;
- case 'b':
- some_digits(rx_bits);
- next_char();
- break;
- case 'o':
- some_digits(rx_octals);
- next_char();
- break;
- case 'x':
- some_digits(rx_hexs);
- next_char();
- break;
- }
- } else {
- next_char();
+ function number() {
+ if (snippet === "0") {
+ next_char();
+ if (char === ".") {
frack();
+ } else if (char === "b") {
+ some_digits(rx_bits);
+ next_char();
+ } else if (char === "o") {
+ some_digits(rx_octals);
+ next_char();
+ } else if (char === "x") {
+ some_digits(rx_hexs);
+ next_char();
}
+ } else {
+ next_char();
+ frack();
+ }
// If the next character after a number is a digit or letter, then something
// unexpected is going on.
- if (
- (char >= '0' && char <= '9') ||
- (char >= 'a' && char <= 'z') ||
- (char >= 'A' && char <= 'Z')
- ) {
- return stop_at(
- 'unexpected_a_after_b',
- line,
- column - 1,
- snippet.slice(-1),
- snippet.slice(0, -1)
- );
- }
- back_char();
- return make('(number)', snippet);
+ if (
+ (char >= "0" && char <= "9")
+ || (char >= "a" && char <= "z")
+ || (char >= "A" && char <= "Z")
+ ) {
+ return stop_at(
+ "unexpected_a_after_b",
+ line,
+ column - 1,
+ snippet.slice(-1),
+ snippet.slice(0, -1)
+ );
}
+ back_char();
+ return make("(number)", snippet);
+ }
- function lex() {
- var array,
- i = 0,
- j = 0,
- last,
- result,
- the_token;
- if (!source_line) {
- source_line = next_line();
- from = 0;
- return (source_line === undefined)
- ? (mega_mode)
- ? stop_at('unclosed_mega', mega_line, mega_from)
- : make('(end)')
- : lex();
- }
- from = column;
- result = source_line.match(rx_token);
+ function lex() {
+ let array;
+ let i = 0;
+ let j = 0;
+ let last;
+ let result;
+ let the_token;
+ if (!source_line) {
+ source_line = next_line();
+ from = 0;
+ return (
+ source_line === undefined
+ ? (
+ mega_mode
+ ? stop_at("unclosed_mega", mega_line, mega_from)
+ : make("(end)")
+ )
+ : lex()
+ );
+ }
+ from = column;
+ result = source_line.match(rx_token);
// result[1] token
// result[2] whitespace
@@ -1236,221 +1267,240 @@ var jslint = (function JSLint() {
// result[4] number
// result[5] rest
- if (!result) {
- return stop_at('unexpected_char_a', line, column, source_line.charAt(0));
- }
+ if (!result) {
+ return stop_at(
+ "unexpected_char_a",
+ line,
+ column,
+ source_line[0]
+ );
+ }
- snippet = result[1];
- column += snippet.length;
- source_line = result[5];
+ snippet = result[1];
+ column += snippet.length;
+ source_line = result[5];
// Whitespace was matched. Call lex again to get more.
- if (result[2]) {
- return lex();
- }
+ if (result[2]) {
+ return lex();
+ }
// The token is an identifier.
- if (result[3]) {
- return make(snippet, undefined, true);
- }
+ if (result[3]) {
+ return make(snippet, undefined, true);
+ }
// The token is a number.
- if (result[4]) {
- return number(snippet);
- }
-
-// The token is something miscellaneous.
-
- switch (snippet) {
+ if (result[4]) {
+ return number(snippet);
+ }
-// The token is a single or double quote string.
+// The token is a string.
- case '\'':
- case '"':
- return string(snippet);
+ if (snippet === "\"") {
+ return string(snippet);
+ }
+ if (snippet === "'") {
+ if (!option.single) {
+ warn_at("use_double", line, column);
+ }
+ return string(snippet);
+ }
-// The token is a megastring. We don't allow any kind if mega nesting.
+// The token is a megastring. We don't allow any kind of mega nesting.
- case '`':
- if (mega_mode) {
- return stop_at('expected_a_b', line, column, '}', '`');
- }
- snippet = '';
- mega_from = from;
- mega_line = line;
- mega_mode = true;
+ if (snippet === "`") {
+ if (mega_mode) {
+ return stop_at("expected_a_b", line, column, "}", "`");
+ }
+ snippet = "";
+ mega_from = from;
+ mega_line = line;
+ mega_mode = true;
// Parsing a mega literal is tricky. First make a ` token.
- make('`');
- from += 1;
+ make("`");
+ from += 1;
// Then loop, building up a string, possibly from many lines, until seeing
// the end of file, a closing `, or a ${ indicting an expression within the
// string.
- (function part() {
- var at = source_line.search(rx_mega);
+ (function part() {
+ const at = source_line.search(rx_mega);
// If neither ` nor ${ is seen, then the whole line joins the snippet.
- if (at < 0) {
- snippet += source_line + '\n';
- return (next_line() === undefined)
- ? stop_at('unclosed_mega', mega_line, mega_from)
- : part();
- }
+ if (at < 0) {
+ snippet += source_line + "\n";
+ return (
+ next_line() === undefined
+ ? stop_at("unclosed_mega", mega_line, mega_from)
+ : part()
+ );
+ }
// if either ` or ${ was found, then the preceding joins the snippet to become
// a string token.
- snippet += source_line.slice(0, at);
- column += at;
- source_line = source_line.slice(at);
- make('(string)', snippet).quote = '`';
- snippet = '';
+ snippet += source_line.slice(0, at);
+ column += at;
+ source_line = source_line.slice(at);
+ if (source_line[0] === "\\") {
+ stop_at("escape_mega", line, at);
+ }
+ make("(string)", snippet).quote = "`";
+ snippet = "";
// If ${, then make tokens that will become part of an expression until
// a } token is made.
- if (source_line.charAt(0) === '$') {
- column += 2;
- make('${');
- source_line = source_line.slice(2);
- (function expr() {
- var id = lex().id;
- if (id === '{') {
- return stop_at(
- 'expected_a_b',
- line,
- column,
- '}',
- '{'
- );
- }
- if (id !== '}') {
- return expr();
- }
- }());
- return part();
- }
- }());
- source_line = source_line.slice(1);
- column += 1;
- mega_mode = false;
- return make('`');
+ if (source_line[0] === "$") {
+ column += 2;
+ make("${");
+ source_line = source_line.slice(2);
+ (function expr() {
+ const id = lex().id;
+ if (id === "{") {
+ return stop_at(
+ "expected_a_b",
+ line,
+ column,
+ "}",
+ "{"
+ );
+ }
+ if (id !== "}") {
+ return expr();
+ }
+ }());
+ return part();
+ }
+ }());
+ source_line = source_line.slice(1);
+ column += 1;
+ mega_mode = false;
+ return make("`");
+ }
// The token is a // comment.
- case '//':
- snippet = source_line;
- source_line = '';
- the_token = comment(snippet);
- if (mega_mode) {
- warn('unexpected_comment', the_token, '`');
- }
- return the_token;
+ if (snippet === "//") {
+ snippet = source_line;
+ source_line = "";
+ the_token = comment(snippet);
+ if (mega_mode) {
+ warn("unexpected_comment", the_token, "`");
+ }
+ return the_token;
+ }
// The token is a /* comment.
- case '/*':
- array = [];
- if (source_line.charAt(0) === '/') {
- warn_at('unexpected_a', line, column + i, '/');
- }
- (function next() {
- if (source_line > '') {
- i = source_line.search(rx_star_slash);
- if (i >= 0) {
- return;
- }
- j = source_line.search(rx_slash_star);
- if (j >= 0) {
- warn_at('nested_comment', line, column + j);
- }
+ if (snippet === "/*") {
+ array = [];
+ if (source_line[0] === "/") {
+ warn_at("unexpected_a", line, column + i, "/");
+ }
+ (function next() {
+ if (source_line > "") {
+ i = source_line.search(rx_star_slash);
+ if (i >= 0) {
+ return;
}
- array.push(source_line);
- source_line = next_line();
- if (source_line === undefined) {
- return stop_at('unclosed_comment', line, column);
+ j = source_line.search(rx_slash_star);
+ if (j >= 0) {
+ warn_at("nested_comment", line, column + j);
}
- return next();
- }());
- snippet = source_line.slice(0, i);
- j = snippet.search(rx_slash_star_or_slash);
- if (j >= 0) {
- warn_at('nested_comment', line, column + j);
}
- array.push(snippet);
- column += i + 2;
- source_line = source_line.slice(i + 2);
- return comment(array);
+ array.push(source_line);
+ source_line = next_line();
+ if (source_line === undefined) {
+ return stop_at("unclosed_comment", line, column);
+ }
+ return next();
+ }());
+ snippet = source_line.slice(0, i);
+ j = snippet.search(rx_slash_star_or_slash);
+ if (j >= 0) {
+ warn_at("nested_comment", line, column + j);
+ }
+ array.push(snippet);
+ column += i + 2;
+ source_line = source_line.slice(i + 2);
+ return comment(array);
+ }
// The token is a slash.
- case '/':
+ if (snippet === "/") {
// The / can be a division operator or the beginning of a regular expression
// literal. It is not possible to know which without doing a complete parse.
// We want to complete the tokenization before we begin to parse, so we will
// estimate. This estimator can fail in some cases. For example, it cannot
-// know if '}' is ending a block or ending an object literal, so it can
+// know if "}" is ending a block or ending an object literal, so it can
// behave incorrectly in that case; it is not meaningful to divide an
// object, so it is likely that we can get away with it. We avoided the worst
// cases by eliminating automatic semicolon insertion.
- if (prior.identifier) {
- if (!prior.dot) {
- switch (prior.id) {
- case 'return':
- return regexp();
- case '(begin)':
- case 'case':
- case 'delete':
- case 'in':
- case 'instanceof':
- case 'new':
- case 'typeof':
- case 'void':
- case 'yield':
- the_token = regexp();
- return stop('unexpected_a', the_token);
- }
- }
- } else {
- last = prior.id.charAt(prior.id.length - 1);
- if ('(,=:?['.indexOf(last) >= 0) {
+ if (prior.identifier) {
+ if (!prior.dot) {
+ if (prior.id === "return") {
return regexp();
}
- if ('!&|{};~+-*%/^<>'.indexOf(last) >= 0) {
+ if (
+ prior.id === "(begin)"
+ || prior.id === "case"
+ || prior.id === "delete"
+ || prior.id === "in"
+ || prior.id === "instanceof"
+ || prior.id === "new"
+ || prior.id === "typeof"
+ || prior.id === "void"
+ || prior.id === "yield"
+ ) {
the_token = regexp();
- warn('wrap_regexp', the_token);
- return the_token;
+ return stop("unexpected_a", the_token);
}
}
- if (source_line.charAt(0) === '/') {
- column += 1;
- source_line = source_line.slice(1);
- snippet = '/=';
- warn_at('unexpected_a', line, column, '/=');
+ } else {
+ last = prior.id[prior.id.length - 1];
+ if ("(,=:?[".indexOf(last) >= 0) {
+ return regexp();
+ }
+ if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
+ the_token = regexp();
+ warn("wrap_regexp", the_token);
+ return the_token;
}
- break;
}
- return make(snippet);
+ if (source_line[0] === "/") {
+ column += 1;
+ source_line = source_line.slice(1);
+ snippet = "/=";
+ warn_at("unexpected_a", line, column, "/=");
+ }
}
+ return make(snippet);
+ }
+
+ first = lex();
+ json_mode = first.id === "{" || first.id === "[";
// This is the only loop in JSLint. It will turn into a recursive call to lex
// when ES6 has been finished and widely deployed and adopted.
- while (true) {
- if (lex().id === '(end)') {
- break;
- }
+ while (true) {
+ if (lex().id === "(end)") {
+ break;
}
}
+}
// Parsing:
@@ -1466,259 +1516,287 @@ var jslint = (function JSLint() {
// Specialized tokens may have additional properties.
- function survey(name) {
- var id = name.id;
+function survey(name) {
+ let id = name.id;
// Tally the property name. If it is a string, only tally strings that conform
// to the identifier rules.
- if (id === '(string)') {
- id = name.value;
+ if (id === "(string)") {
+ id = name.value;
+ if (!rx_identifier.test(id)) {
+ return id;
+ }
+ } else if (id === "`") {
+ if (name.value.length === 1) {
+ id = name.value[0].value;
if (!rx_identifier.test(id)) {
return id;
}
- } else {
- if (!name.identifier) {
- return stop('expected_identifier_a', name);
- }
}
+ } else if (!name.identifier) {
+ return stop("expected_identifier_a", name);
+ }
// If we have seen this name before, increment its count.
- if (typeof property[id] === 'number') {
- property[id] += 1;
+ if (typeof property[id] === "number") {
+ property[id] += 1;
// If this is the first time seeing this property name, and if there is a
// tenure list, then it must be on the list. Otherwise, it must conform to
// the rules for good property names.
+ } else {
+ if (tenure !== undefined) {
+ if (tenure[id] !== true) {
+ warn("unregistered_property_a", name);
+ }
} else {
- if (tenure !== undefined) {
- if (tenure[id] !== true) {
- warn('unregistered_property_a', name);
- }
- } else {
- if (name.identifier && rx_bad_property.test(id)) {
- warn('bad_property_a', name);
- }
+ if (name.identifier && rx_bad_property.test(id)) {
+ warn("bad_property_a", name);
}
- property[id] = 1;
}
- return id;
+ property[id] = 1;
}
+ return id;
+}
- function dispense() {
+function dispense() {
// Deliver the next token, skipping the comments.
- var cadet = tokens[token_nr];
- token_nr += 1;
- if (cadet.id === '(comment)') {
- if (json_mode) {
- warn('unexpected_a', cadet);
- }
- return dispense();
- } else {
- return cadet;
+ const cadet = tokens[token_nr];
+ token_nr += 1;
+ if (cadet.id === "(comment)") {
+ if (json_mode) {
+ warn("unexpected_a", cadet);
}
+ return dispense();
+ } else {
+ return cadet;
}
+}
- function lookahead() {
+function lookahead() {
// Look ahead one token without advancing.
- var old_token_nr = token_nr,
- cadet = dispense(true);
- token_nr = old_token_nr;
- return cadet;
- }
+ const old_token_nr = token_nr;
+ const cadet = dispense(true);
+ token_nr = old_token_nr;
+ return cadet;
+}
- function advance(id, match) {
+function advance(id, match) {
// Produce the next token.
// Attempt to give helpful names to anonymous functions.
- if (token.identifier && token.id !== 'function') {
- anon = token.id;
- } else if (token.id === '(string)' && rx_identifier.test(token.value)) {
- anon = token.value;
- }
+ if (token.identifier && token.id !== "function") {
+ anon = token.id;
+ } else if (token.id === "(string)" && rx_identifier.test(token.value)) {
+ anon = token.value;
+ }
// Attempt to match next_token with an expected id.
- if (id !== undefined && next_token.id !== id) {
- return (match === undefined)
- ? stop('expected_a_b', next_token, id, artifact())
- : stop(
- 'expected_a_b_from_c_d',
- next_token,
- id,
- artifact(match),
- artifact_line(match),
- artifact(next_token)
- );
- }
+ if (id !== undefined && next_token.id !== id) {
+ return (
+ match === undefined
+ ? stop("expected_a_b", next_token, id, artifact())
+ : stop(
+ "expected_a_b_from_c_d",
+ next_token,
+ id,
+ artifact(match),
+ artifact_line(match),
+ artifact(next_token)
+ )
+ );
+ }
// Promote the tokens, skipping comments.
- token = next_token;
- next_token = dispense();
- if (next_token.id === '(end)') {
- token_nr -= 1;
- }
+ token = next_token;
+ next_token = dispense();
+ if (next_token.id === "(end)") {
+ token_nr -= 1;
}
+}
// Parsing of JSON is simple:
- function json_value() {
-
- function json_object() {
- var brace = next_token,
- object = empty();
- advance('{');
- if (next_token.id !== '}') {
+function json_value() {
+ let negative;
+ if (next_token.id === "{") {
+ return (function json_object() {
+ const brace = next_token;
+ const object = empty();
+ const properties = [];
+ brace.expression = properties;
+ advance("{");
+ if (next_token.id !== "}") {
(function next() {
- if (next_token.quote !== '"') {
- warn('unexpected_a', next_token, next_token.quote);
+ let name;
+ let value;
+ if (next_token.quote !== "\"") {
+ warn(
+ "unexpected_a",
+ next_token,
+ next_token.quote
+ );
}
- advance('(string)');
+ name = next_token;
+ advance("(string)");
if (object[token.value] !== undefined) {
- warn('duplicate_a', token);
- } else if (token.value === '__proto__') {
- warn('bad_property_name_a', token);
+ warn("duplicate_a", token);
+ } else if (token.value === "__proto__") {
+ warn("bad_property_a", token);
} else {
object[token.value] = token;
}
- advance(':');
- json_value();
- if (next_token.id === ',') {
- advance(',');
+ advance(":");
+ value = json_value();
+ value.label = name;
+ properties.push(value);
+ if (next_token.id === ",") {
+ advance(",");
return next();
}
}());
}
- advance('}', brace);
- }
-
- function json_array() {
- var bracket = next_token;
- advance('[');
- if (next_token.id !== ']') {
+ advance("}", brace);
+ return brace;
+ }());
+ }
+ if (next_token.id === "[") {
+ return (function json_array() {
+ const bracket = next_token;
+ const elements = [];
+ bracket.expression = elements;
+ advance("[");
+ if (next_token.id !== "]") {
(function next() {
- json_value();
- if (next_token.id === ',') {
- advance(',');
+ elements.push(json_value());
+ if (next_token.id === ",") {
+ advance(",");
return next();
}
}());
}
- advance(']', bracket);
+ advance("]", bracket);
+ return bracket;
+ }());
+ }
+ if (
+ next_token.id === "true"
+ || next_token.id === "false"
+ || next_token.id === "null"
+ ) {
+ advance();
+ return token;
+ }
+ if (next_token.id === "(number)") {
+ if (!rx_JSON_number.test(next_token.value)) {
+ warn("unexpected_a");
}
-
- switch (next_token.id) {
- case '{':
- json_object();
- break;
- case '[':
- json_array();
- break;
- case 'true':
- case 'false':
- case 'null':
- advance();
- break;
- case '(number)':
- if (!rx_JSON_number.test(next_token.value)) {
- warn('unexpected_a');
- }
- advance();
- break;
- case '(string)':
- if (next_token.quote !== '"') {
- warn('unexpected_a', next_token, next_token.quote);
- }
- advance();
- break;
- case '-':
- advance('-');
- advance('(number)');
- break;
- default:
- stop('unexpected_a');
+ advance();
+ return token;
+ }
+ if (next_token.id === "(string)") {
+ if (next_token.quote !== "\"") {
+ warn("unexpected_a", next_token, next_token.quote);
}
+ advance();
+ return token;
}
+ if (next_token.id === "-") {
+ negative = next_token;
+ negative.arity = "unary";
+ advance("-");
+ advance("(number)");
+ negative.expression = token;
+ return negative;
+ }
+ stop("unexpected_a");
+}
// Now we parse JavaScript.
- function enroll(name, role, readonly) {
+function enroll(name, role, readonly) {
// Enroll a name into the current function context. The role can be exception,
// function, label, parameter, or variable. We look for variable redefinition
// because it causes confusion.
- var id = name.id;
+ const id = name.id;
// Reserved words may not be enrolled.
- if (syntax[id] !== undefined && id !== 'ignore') {
- warn('reserved_a', name);
- } else {
+ if (syntax[id] !== undefined && id !== "ignore") {
+ warn("reserved_a", name);
+ } else {
// Has the name been enrolled in this context?
- var earlier = functionage.context[id];
- if (earlier) {
- warn(
- 'redefinition_a_b',
- name,
- name.id,
- earlier.line + fudge
- );
+ let earlier = functionage.context[id];
+ if (earlier) {
+ warn(
+ "redefinition_a_b",
+ name,
+ name.id,
+ earlier.line + fudge
+ );
// Has the name been enrolled in an outer context?
- } else {
- stack.forEach(function (value) {
- var item = value.context[id];
- if (item !== undefined) {
- earlier = item;
+ } else {
+ stack.forEach(function (value) {
+ const item = value.context[id];
+ if (item !== undefined) {
+ earlier = item;
+ }
+ });
+ if (earlier) {
+ if (id === "ignore") {
+ if (earlier.role === "variable") {
+ warn("unexpected_a", name);
}
- });
- if (earlier) {
- if (id === 'ignore') {
- if (earlier.role === 'variable') {
- warn('unexpected_a', name);
- }
- } else {
- if ((
- role !== 'exception' ||
- earlier.role !== 'exception'
- ) && role !== 'parameter') {
- warn(
- 'redefinition_a_b',
- name,
- name.id,
- earlier.line + fudge
- );
- }
+ } else {
+ if (
+ (
+ role !== "exception"
+ || earlier.role !== "exception"
+ )
+ && role !== "parameter"
+ && role !== "function"
+ ) {
+ warn(
+ "redefinition_a_b",
+ name,
+ name.id,
+ earlier.line + fudge
+ );
}
}
+ }
// Enroll it.
- functionage.context[id] = name;
- name.dead = true;
- name.function = functionage;
- name.init = false;
- name.role = role;
- name.used = 0;
- name.writable = !readonly;
- }
+ functionage.context[id] = name;
+ name.dead = true;
+ name.parent = functionage;
+ name.init = false;
+ name.role = role;
+ name.used = 0;
+ name.writable = !readonly;
}
}
+}
- function expression(rbp, initial) {
+function expression(rbp, initial) {
// This is the heart of the Pratt parser. I retained Pratt's nomenclature.
// They are elements of the parsing method called Top Down Operator Precedence.
@@ -1732,2767 +1810,3138 @@ var jslint = (function JSLint() {
// process leds (infix operators) until the bind powers cause it to stop. It
// returns the expression's parse tree.
- var left, the_symbol;
+ let left;
+ let the_symbol;
// Statements will have already advanced, so advance now only if the token is
// not the first of a statement,
- if (!initial) {
+ if (!initial) {
+ advance();
+ }
+ the_symbol = syntax[token.id];
+ if (the_symbol !== undefined && the_symbol.nud !== undefined) {
+ left = the_symbol.nud();
+ } else if (token.identifier) {
+ left = token;
+ left.arity = "variable";
+ } else {
+ return stop("unexpected_a", token);
+ }
+ (function right() {
+ the_symbol = syntax[next_token.id];
+ if (
+ the_symbol !== undefined
+ && the_symbol.led !== undefined
+ && rbp < the_symbol.lbp
+ ) {
advance();
+ left = the_symbol.led(left);
+ return right();
}
- the_symbol = syntax[token.id];
- if (the_symbol !== undefined && the_symbol.nud !== undefined) {
- left = the_symbol.nud();
- } else if (token.identifier) {
- left = token;
- left.arity = 'variable';
- } else {
- return stop('unexpected_a', token);
- }
- (function right() {
- the_symbol = syntax[next_token.id];
- if (
- the_symbol !== undefined &&
- the_symbol.led !== undefined &&
- rbp < the_symbol.lbp
- ) {
- advance();
- left = the_symbol.led(left);
- return right();
- }
- }());
- return left;
- }
+ }());
+ return left;
+}
- function condition() {
+function condition() {
// Parse the condition part of a do, if, while.
- var the_paren = next_token,
- the_value;
- the_paren.free = true;
- advance('(');
- the_value = expression(0);
- advance(')');
- if (the_value.wrapped === true) {
- warn('unexpected_a', the_paren);
- }
- switch (the_value.id) {
- case '?':
- case '~':
- case '&':
- case '|':
- case '^':
- case '<<':
- case '>>':
- case '>>>':
- case '+':
- case '-':
- case '*':
- case '/':
- case '%':
- case 'typeof':
- case '(number)':
- case '(string)':
- warn('unexpected_a', the_value);
- break;
- }
- return the_value;
+ const the_paren = next_token;
+ let the_value;
+ the_paren.free = true;
+ advance("(");
+ the_value = expression(0);
+ advance(")");
+ if (the_value.wrapped === true) {
+ warn("unexpected_a", the_paren);
}
-
- function is_weird(thing) {
+ if (anticondition[the_value.id] === true) {
+ warn("unexpected_a", the_value);
+ }
+ return the_value;
+}
+
+function is_weird(thing) {
+ return (
+ thing.id === "(regexp)"
+ || thing.id === "{"
+ || thing.id === "=>"
+ || thing.id === "function"
+ || (thing.id === "[" && thing.arity === "unary")
+ );
+}
+
+function are_similar(a, b) {
+ if (a === b) {
+ return true;
+ }
+ if (Array.isArray(a)) {
return (
- thing.id === '(regexp)' ||
- thing.id === '{' ||
- thing.id === '=>' ||
- thing.id === 'function' ||
- (thing.id === '[' && thing.arity === 'unary')
+ Array.isArray(b)
+ && a.length === b.length
+ && a.every(function (value, index) {
+ return are_similar(value, b[index]);
+ })
);
}
-
- function are_similar(a, b) {
- if (a === b) {
- return true;
- }
- if (Array.isArray(a)) {
+ if (Array.isArray(b)) {
+ return false;
+ }
+ if (a.id === "(number)" && b.id === "(number)") {
+ return a.value === b.value;
+ }
+ let a_string;
+ let b_string;
+ if (a.id === "(string)") {
+ a_string = a.value;
+ } else if (a.id === "`" && a.constant) {
+ a_string = a.value[0];
+ }
+ if (b.id === "(string)") {
+ b_string = b.value;
+ } else if (b.id === "`" && b.constant) {
+ b_string = b.value[0];
+ }
+ if (typeof a_string === "string") {
+ return a_string === b_string;
+ }
+ if (is_weird(a) || is_weird(b)) {
+ return false;
+ }
+ if (a.arity === b.arity && a.id === b.id) {
+ if (a.id === ".") {
return (
- Array.isArray(b) &&
- a.length === b.length &&
- a.every(function (value, index) {
- return are_similar(value, b[index]);
- })
+ are_similar(a.expression, b.expression)
+ && are_similar(a.name, b.name)
);
}
- if (Array.isArray(b)) {
- return false;
- }
- if (a.id === '(number)' && b.id === '(number)') {
- return a.value === b.value;
+ if (a.arity === "unary") {
+ return are_similar(a.expression, b.expression);
}
- var a_string, b_string;
- if (a.id === '(string)') {
- a_string = a.value;
- } else if (a.id === '`' && a.constant) {
- a_string = a.value[0];
- }
- if (b.id === '(string)') {
- b_string = b.value;
- } else if (b.id === '`' && b.constant) {
- b_string = b.value[0];
+ if (a.arity === "binary") {
+ return (
+ a.id !== "("
+ && are_similar(a.expression[0], b.expression[0])
+ && are_similar(a.expression[1], b.expression[1])
+ );
}
- if (typeof a_string === 'string') {
- return a_string === b_string;
+ if (a.arity === "ternary") {
+ return (
+ are_similar(a.expression[0], b.expression[0])
+ && are_similar(a.expression[1], b.expression[1])
+ && are_similar(a.expression[2], b.expression[2])
+ );
}
- if (is_weird(a) || is_weird(b)) {
+ if (a.arity === "function" && a.arity === "regexp") {
return false;
}
- if (a.arity === b.arity && a.id === b.id) {
- if (a.id === '.') {
- return are_similar(a.expression, b.expression) &&
- are_similar(a.name, b.name);
- }
- switch (a.arity) {
- case 'unary':
- return are_similar(a.expression, b.expression);
- case 'binary':
- return a.id !== '(' &&
- are_similar(a.expression[0], b.expression[0]) &&
- are_similar(a.expression[1], b.expression[1]);
- case 'ternary':
- return are_similar(a.expression[0], b.expression[0]) &&
- are_similar(a.expression[1], b.expression[1]) &&
- are_similar(a.expression[2], b.expression[2]);
- case 'function':
- case 'regexp':
- return false;
- default:
- return true;
- }
- }
- return false;
+ return true;
}
+ return false;
+}
- function semicolon() {
+function semicolon() {
// Try to match a semicolon.
- if (next_token.id === ';') {
- advance(';');
- } else {
- warn_at(
- 'expected_a_b',
- token.line,
- token.thru,
- ';',
- artifact(next_token)
- );
- }
- anon = 'anonymous';
+ if (next_token.id === ";") {
+ advance(";");
+ } else {
+ warn_at(
+ "expected_a_b",
+ token.line,
+ token.thru,
+ ";",
+ artifact(next_token)
+ );
}
+ anon = "anonymous";
+}
- function statement() {
+function statement() {
// Parse a statement. Any statement may have a label, but only four statements
// have use for one. A statement can be one of the standard statements, or
// an assignment expression, or an invocation expression.
- var first,
- the_label,
- the_statement,
- the_symbol;
- advance();
- if (token.identifier && next_token.id === ':') {
- the_label = token;
- if (the_label.id === 'ignore') {
- warn('unexpected_a', the_label);
- }
- advance(':');
- switch (next_token.id) {
- case 'do':
- case 'for':
- case 'switch':
- case 'while':
- enroll(the_label, 'label', true);
- the_label.init = true;
- the_label.dead = false;
- the_statement = statement();
- the_statement.label = the_label;
- the_statement.statement = true;
- return the_statement;
- default:
- advance();
- warn('unexpected_label_a', the_label);
- }
+ let first;
+ let the_label;
+ let the_statement;
+ let the_symbol;
+ advance();
+ if (token.identifier && next_token.id === ":") {
+ the_label = token;
+ if (the_label.id === "ignore") {
+ warn("unexpected_a", the_label);
+ }
+ advance(":");
+ if (
+ next_token.id === "do"
+ || next_token.id === "for"
+ || next_token.id === "switch"
+ || next_token.id === "while"
+ ) {
+ enroll(the_label, "label", true);
+ the_label.init = true;
+ the_label.dead = false;
+ the_statement = statement();
+ the_statement.label = the_label;
+ the_statement.statement = true;
+ return the_statement;
}
+ advance();
+ warn("unexpected_label_a", the_label);
+ }
// Parse the statement.
- first = token;
- first.statement = true;
- the_symbol = syntax[first.id];
- if (the_symbol !== undefined && the_symbol.fud !== undefined) {
- the_symbol.disrupt = false;
- the_symbol.statement = true;
- the_statement = the_symbol.fud();
- } else {
+ first = token;
+ first.statement = true;
+ the_symbol = syntax[first.id];
+ if (the_symbol !== undefined && the_symbol.fud !== undefined) {
+ the_symbol.disrupt = false;
+ the_symbol.statement = true;
+ the_statement = the_symbol.fud();
+ } else {
// It is an expression statement.
- the_statement = expression(0, true);
- if (the_statement.wrapped && the_statement.id !== '(') {
- warn('unexpected_a', first);
- }
- semicolon();
+ the_statement = expression(0, true);
+ if (the_statement.wrapped && the_statement.id !== "(") {
+ warn("unexpected_a", first);
}
- if (the_label !== undefined) {
- the_label.dead = true;
- }
- return the_statement;
+ semicolon();
}
+ if (the_label !== undefined) {
+ the_label.dead = true;
+ }
+ return the_statement;
+}
- function statements() {
+function statements() {
// Parse a list of statements. Give a warning if an unreachable statement
// follows a disruptive statement.
- var array = [];
- (function next(disrupt) {
- var a_statement;
- switch (next_token.id) {
- case '}':
- case 'case':
- case 'default':
- case 'else':
- case '(end)':
- break;
- default:
- a_statement = statement();
- array.push(a_statement);
- if (disrupt) {
- warn('unreachable_a', a_statement);
- }
- return next(a_statement.disrupt);
+ const array = [];
+ (function next(disrupt) {
+ if (
+ next_token.id !== "}"
+ && next_token.id !== "case"
+ && next_token.id !== "default"
+ && next_token.id !== "else"
+ && next_token.id !== "(end)"
+ ) {
+ let a_statement = statement();
+ array.push(a_statement);
+ if (disrupt) {
+ warn("unreachable_a", a_statement);
}
- }(false));
- return array;
- }
+ return next(a_statement.disrupt);
+ }
+ }(false));
+ return array;
+}
- function not_top_level(thing) {
+function not_top_level(thing) {
// Some features should not be at the outermost level.
- if (functionage === global) {
- warn('unexpected_at_top_level_a', thing);
- }
+ if (functionage === global) {
+ warn("unexpected_at_top_level_a", thing);
}
+}
- function top_level_only(the_thing) {
+function top_level_only(the_thing) {
// Some features must be at the most outermost level.
- if (blockage !== global) {
- warn('misplaced_a', the_thing);
- }
+ if (blockage !== global) {
+ warn("misplaced_a", the_thing);
}
+}
- function block(special) {
+function block(special) {
// Parse a block, a sequence of statements wrapped in braces.
-// special 'body' The block is a function body.
-// 'ignore' No warning on an empty block.
-// 'naked' No advance.
-// undefined Not special.
-
- var stmts, the_block;
- if (special !== 'naked') {
- advance('{');
- }
- the_block = token;
- the_block.arity = 'statement';
- the_block.body = special === 'body';
-
-// All top level function bodies should include the 'use strict' pragma unless
-// the whole file is strict.
-
- if (the_block.body && stack.length <= 1 && !global.strict) {
- if (
- next_token.id === '(string)' ||
- next_token.value === 'use strict'
- ) {
- next_token.statement = true;
- functionage.strict = true;
- advance('(string)');
- advance(';');
- } else {
- warn(
- 'expected_a_before_b',
- next_token,
- (next_token.id === '`')
- ? '\''
- : 'use strict',
- artifact(next_token)
- );
- }
- }
- stmts = statements();
- the_block.block = stmts;
- if (stmts.length === 0) {
- if (!option.devel && special !== 'ignore') {
- warn('empty_block', the_block);
- }
- the_block.disrupt = false;
- } else {
- the_block.disrupt = stmts[stmts.length - 1].disrupt;
- }
- advance('}');
- return the_block;
+// special "body" The block is a function body.
+// "ignore" No warning on an empty block.
+// "naked" No advance.
+// undefined An ordinary block.
+
+ let stmts;
+ let the_block;
+ if (special !== "naked") {
+ advance("{");
+ }
+ the_block = token;
+ the_block.arity = "statement";
+ the_block.body = special === "body";
+
+// Top level function bodies may include the "use strict" pragma.
+
+ if (
+ special === "body"
+ && stack.length === 1
+ && next_token.value === "use strict"
+ ) {
+ next_token.statement = true;
+ advance("(string)");
+ advance(";");
}
+ stmts = statements();
+ the_block.block = stmts;
+ if (stmts.length === 0) {
+ if (!option.devel && special !== "ignore") {
+ warn("empty_block", the_block);
+ }
+ the_block.disrupt = false;
+ } else {
+ the_block.disrupt = stmts[stmts.length - 1].disrupt;
+ }
+ advance("}");
+ return the_block;
+}
- function mutation_check(the_thing) {
+function mutation_check(the_thing) {
// The only expressions that may be assigned to are
// e.b
// e[b]
// v
-
- if (
- the_thing.id !== '.' &&
- (the_thing.id !== '[' || the_thing.arity !== 'binary') &&
- the_thing.arity !== 'variable'
- ) {
- warn('bad_assignment_a', the_thing);
- return false;
- }
- return true;
+// [destructure]
+// {destructure}
+
+ if (
+ the_thing.arity !== "variable"
+ && the_thing.id !== "."
+ && the_thing.id !== "["
+ && the_thing.id !== "{"
+ ) {
+ warn("bad_assignment_a", the_thing);
+ return false;
}
+ return true;
+}
- function left_check(left, right) {
+function left_check(left, right) {
// Warn if the left is not one of these:
// e.b
// e[b]
// e()
+// ?:
// identifier
- var id = left.id;
- if (
- !left.identifier &&
- (
- left.arity !== 'binary' ||
- (id !== '.' && id !== '(' && id !== '[')
+ const id = left.id;
+ if (
+ !left.identifier
+ && (
+ left.arity !== "ternary"
+ || (
+ !left_check(left.expression[1])
+ && !left_check(left.expression[2])
)
- ) {
- warn('unexpected_a', right);
- return false;
- }
- return true;
+ )
+ && (
+ left.arity !== "binary"
+ || (id !== "." && id !== "(" && id !== "[")
+ )
+ ) {
+ warn("unexpected_a", right);
+ return false;
}
+ return true;
+}
// These functions are used to specify the grammar of our language:
- function symbol(id, bp) {
+function symbol(id, bp) {
// Make a symbol if it does not already exist in the language's syntax.
- var the_symbol = syntax[id];
- if (the_symbol === undefined) {
- the_symbol = empty();
- the_symbol.id = id;
- the_symbol.lbp = bp || 0;
- syntax[id] = the_symbol;
- }
- return the_symbol;
+ let the_symbol = syntax[id];
+ if (the_symbol === undefined) {
+ the_symbol = empty();
+ the_symbol.id = id;
+ the_symbol.lbp = bp || 0;
+ syntax[id] = the_symbol;
}
+ return the_symbol;
+}
- function assignment(id) {
+function assignment(id) {
// Make an assignment operator. The one true assignment is different because
// its left side, when it is a variable, is not treated as an expression.
// That case is special because that is when a variable gets initialized. The
// other assignment operators can modify, but they cannot initialize.
- var the_symbol = symbol(id, 20);
- the_symbol.led = function (left) {
- var the_token = token,
- right;
- the_token.arity = 'assignment';
- right = expression(20 - 1);
- if (id === '=' && left.arity === 'variable') {
- the_token.names = left;
- the_token.expression = right;
- } else {
- the_token.expression = [left, right];
- }
- switch (right.arity) {
- case 'assignment':
- case 'pre':
- case 'post':
- warn('unexpected_a', right);
- break;
- }
- if (
- option.es6 &&
- left.arity === 'unary' &&
- (left.id === '[' || left.id === '{')
- ) {
- warn('expected_a_before_b', left, 'const', left.id);
- } else {
- mutation_check(left);
- }
- return the_token;
- };
- return the_symbol;
- }
+ const the_symbol = symbol(id, 20);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ let right;
+ the_token.arity = "assignment";
+ right = expression(20 - 1);
+ if (id === "=" && left.arity === "variable") {
+ the_token.names = left;
+ the_token.expression = right;
+ } else {
+ the_token.expression = [left, right];
+ }
+ if (
+ right.arity === "assignment"
+ || right.arity === "pre"
+ || right.arity === "post"
+ ) {
+ warn("unexpected_a", right);
+ }
+ mutation_check(left);
+ return the_token;
+ };
+ return the_symbol;
+}
- function constant(id, type, value) {
+function constant(id, type, value) {
// Make a constant symbol.
- var the_symbol = symbol(id);
- the_symbol.nud = (typeof value === 'function')
- ? value
- : function () {
- token.constant = true;
- if (value !== undefined) {
- token.value = value;
- }
- return token;
- };
- the_symbol.type = type;
- the_symbol.value = value;
- return the_symbol;
- }
+ const the_symbol = symbol(id);
+ the_symbol.constant = true;
+ the_symbol.nud = (
+ typeof value === "function"
+ ? value
+ : function () {
+ token.constant = true;
+ if (value !== undefined) {
+ token.value = value;
+ }
+ return token;
+ }
+ );
+ the_symbol.type = type;
+ the_symbol.value = value;
+ return the_symbol;
+}
- function infix(id, bp, f) {
+function infix(id, bp, f) {
// Make an infix operator.
- var the_symbol = symbol(id, bp);
- the_symbol.led = function (left) {
- var the_token = token;
- the_token.arity = 'binary';
- if (f !== undefined) {
- return f(left);
- }
- the_token.expression = [left, expression(bp)];
- return the_token;
- };
- return the_symbol;
- }
+ const the_symbol = symbol(id, bp);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ the_token.arity = "binary";
+ if (f !== undefined) {
+ return f(left);
+ }
+ the_token.expression = [left, expression(bp)];
+ return the_token;
+ };
+ return the_symbol;
+}
+
+function infixr(id, bp) {
+
+// Make a right associative infix operator.
+
+ const the_symbol = symbol(id, bp);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ the_token.arity = "binary";
+ the_token.expression = [left, expression(bp - 1)];
+ return the_token;
+ };
+ return the_symbol;
+}
- function post(id) {
+function post(id) {
// Make one of the post operators.
- var the_symbol = symbol(id, 150);
- the_symbol.led = function (left) {
- token.expression = left;
- token.arity = 'post';
- mutation_check(token.expression);
- return token;
- };
- return the_symbol;
- }
+ const the_symbol = symbol(id, 150);
+ the_symbol.led = function (left) {
+ token.expression = left;
+ token.arity = "post";
+ mutation_check(token.expression);
+ return token;
+ };
+ return the_symbol;
+}
- function pre(id) {
+function pre(id) {
// Make one of the pre operators.
- var the_symbol = symbol(id);
- the_symbol.nud = function () {
- var the_token = token;
- the_token.arity = 'pre';
- the_token.expression = expression(150);
- mutation_check(the_token.expression);
- return the_token;
- };
- return the_symbol;
- }
+ const the_symbol = symbol(id);
+ the_symbol.nud = function () {
+ const the_token = token;
+ the_token.arity = "pre";
+ the_token.expression = expression(150);
+ mutation_check(the_token.expression);
+ return the_token;
+ };
+ return the_symbol;
+}
- function prefix(id, f) {
+function prefix(id, f) {
// Make a prefix operator.
- var the_symbol = symbol(id);
- the_symbol.nud = function () {
- var the_token = token;
- the_token.arity = 'unary';
- if (typeof f === 'function') {
- return f();
- }
- the_token.expression = expression(150);
- return the_token;
- };
- return the_symbol;
- }
+ const the_symbol = symbol(id);
+ the_symbol.nud = function () {
+ const the_token = token;
+ the_token.arity = "unary";
+ if (typeof f === "function") {
+ return f();
+ }
+ the_token.expression = expression(150);
+ return the_token;
+ };
+ return the_symbol;
+}
- function stmt(id, f) {
+function stmt(id, f) {
// Make a statement.
- var the_symbol = symbol(id);
- the_symbol.fud = function () {
- token.arity = 'statement';
- return f();
- };
- return the_symbol;
- }
+ const the_symbol = symbol(id);
+ the_symbol.fud = function () {
+ token.arity = "statement";
+ return f();
+ };
+ return the_symbol;
+}
- function ternary(id1, id2) {
+function ternary(id1, id2) {
// Make a ternary operator.
- var the_symbol = symbol(id1, 30);
- the_symbol.led = function (left) {
- var the_token = token,
- second = expression(20);
- advance(id2);
- token.arity = 'ternary';
- the_token.arity = 'ternary';
- the_token.expression = [left, second, expression(10)];
- return the_token;
- };
- return the_symbol;
- }
+ const the_symbol = symbol(id1, 30);
+ the_symbol.led = function (left) {
+ const the_token = token;
+ const second = expression(20);
+ advance(id2);
+ token.arity = "ternary";
+ the_token.arity = "ternary";
+ the_token.expression = [left, second, expression(10)];
+ if (next_token.id !== ")") {
+ warn("use_open", the_token);
+ }
+ return the_token;
+ };
+ return the_symbol;
+}
// Begin defining the language.
- syntax = empty();
-
- symbol('}');
- symbol(')');
- symbol(']');
- symbol(',');
- symbol(';');
- symbol(':');
- symbol('*/');
- symbol('await');
- symbol('case');
- symbol('catch');
- symbol('class');
- symbol('default');
- symbol('else');
- symbol('enum');
- symbol('finally');
- symbol('implements');
- symbol('interface');
- symbol('package');
- symbol('private');
- symbol('protected');
- symbol('public');
- symbol('static');
- symbol('super');
- symbol('void');
- symbol('yield');
-
- constant('(number)', 'number');
- constant('(regexp)', 'regexp');
- constant('(string)', 'string');
- constant('arguments', 'object', function () {
- warn('unexpected_a', token);
- return token;
- });
- constant('eval', 'function', function () {
- if (!option.eval) {
- warn('unexpected_a', token);
- } else if (next_token.id !== '(') {
- warn('expected_a_before_b', next_token, '(', artifact());
- }
- return token;
- });
- constant('false', 'boolean', false);
- constant('ignore', 'undefined', function () {
- warn('unexpected_a', token);
- return token;
- });
- constant('Infinity', 'number', Infinity);
- constant('NaN', 'number', NaN);
- constant('null', 'null', null);
- constant('this', 'object', function () {
- if (!option.this) {
- warn('unexpected_a', token);
- }
- return token;
- });
- constant('true', 'boolean', true);
- constant('undefined', 'undefined');
-
- assignment('=');
- assignment('+=');
- assignment('-=');
- assignment('*=');
- assignment('/=');
- assignment('%=');
- assignment('&=');
- assignment('|=');
- assignment('^=');
- assignment('<<=');
- assignment('>>=');
- assignment('>>>=');
-
- infix('||', 40);
- infix('&&', 50);
- infix('|', 70);
- infix('^', 80);
- infix('&', 90);
- infix('==', 100);
- infix('===', 100);
- infix('!=', 100);
- infix('!==', 100);
- infix('<', 110);
- infix('>', 110);
- infix('<=', 110);
- infix('>=', 110);
- infix('in', 110);
- infix('instanceof', 110);
- infix('<<', 120);
- infix('>>', 120);
- infix('>>>', 120);
- infix('+', 130);
- infix('-', 130);
- infix('*', 140);
- infix('/', 140);
- infix('%', 140);
- infix('(', 160, function (left) {
- var the_paren = token,
- the_argument;
- if (left.id !== 'function') {
- left_check(left, the_paren);
- }
- the_paren.expression = [left];
- if (left.identifier) {
- if (left.new) {
- if (
- left.id.charAt(0) > 'Z' ||
- left.id === 'Boolean' ||
- left.id === 'Number' ||
- left.id === 'String' ||
- (left.id === 'Symbol' && option.es6)
- ) {
- warn('unexpected_a', left, 'new');
- } else if (left.id === 'Function') {
- if (!option.eval) {
- warn('unexpected_a', left, 'new Function');
- }
- } else if (left.id === 'Array') {
- warn('expected_a_b', left, '[]', 'new Array');
- } else if (left.id === 'Object') {
- warn(
- 'expected_a_b',
- left,
- 'Object.create(null)',
- 'new Object'
- );
- }
- } else {
- if (
- left.id.charAt(0) >= 'A' &&
- left.id.charAt(0) <= 'Z' &&
- left.id !== 'Boolean' &&
- left.id !== 'Number' &&
- left.id !== 'String' &&
- left.id !== 'Symbol'
- ) {
- warn(
- 'expected_a_before_b',
- left,
- 'new',
- artifact(left)
- );
- }
- if (functionage.arity === 'statement') {
- functionage.name.calls[left.id] = left;
- }
+syntax = empty();
+
+symbol("}");
+symbol(")");
+symbol("]");
+symbol(",");
+symbol(";");
+symbol(":");
+symbol("*/");
+symbol("await");
+symbol("case");
+symbol("catch");
+symbol("class");
+symbol("default");
+symbol("else");
+symbol("enum");
+symbol("finally");
+symbol("implements");
+symbol("interface");
+symbol("package");
+symbol("private");
+symbol("protected");
+symbol("public");
+symbol("static");
+symbol("super");
+symbol("void");
+symbol("yield");
+
+constant("(number)", "number");
+constant("(regexp)", "regexp");
+constant("(string)", "string");
+constant("arguments", "object", function () {
+ warn("unexpected_a", token);
+ return token;
+});
+constant("eval", "function", function () {
+ if (!option.eval) {
+ warn("unexpected_a", token);
+ } else if (next_token.id !== "(") {
+ warn("expected_a_before_b", next_token, "(", artifact());
+ }
+ return token;
+});
+constant("false", "boolean", false);
+constant("Function", "function", function () {
+ if (!option.eval) {
+ warn("unexpected_a", token);
+ } else if (next_token.id !== "(") {
+ warn("expected_a_before_b", next_token, "(", artifact());
+ }
+ return token;
+});
+constant("ignore", "undefined", function () {
+ warn("unexpected_a", token);
+ return token;
+});
+constant("Infinity", "number", Infinity);
+constant("isFinite", "function", function () {
+ warn("expected_a_b", token, "Number.isFinite", "isFinite");
+ return token;
+});
+constant("isNaN", "function", function () {
+ warn("number_isNaN", token);
+ return token;
+});
+constant("NaN", "number", NaN);
+constant("null", "null", null);
+constant("this", "object", function () {
+ if (!option.this) {
+ warn("unexpected_a", token);
+ }
+ return token;
+});
+constant("true", "boolean", true);
+constant("undefined", "undefined");
+
+assignment("=");
+assignment("+=");
+assignment("-=");
+assignment("*=");
+assignment("/=");
+assignment("%=");
+assignment("&=");
+assignment("|=");
+assignment("^=");
+assignment("<<=");
+assignment(">>=");
+assignment(">>>=");
+
+infix("||", 40);
+infix("&&", 50);
+infix("|", 70);
+infix("^", 80);
+infix("&", 90);
+infix("==", 100);
+infix("===", 100);
+infix("!=", 100);
+infix("!==", 100);
+infix("<", 110);
+infix(">", 110);
+infix("<=", 110);
+infix(">=", 110);
+infix("in", 110);
+infix("instanceof", 110);
+infix("<<", 120);
+infix(">>", 120);
+infix(">>>", 120);
+infix("+", 130);
+infix("-", 130);
+infix("*", 140);
+infix("/", 140);
+infix("%", 140);
+infixr("**", 150);
+infix("(", 160, function (left) {
+ const the_paren = token;
+ let the_argument;
+ if (left.id !== "function") {
+ left_check(left, the_paren);
+ }
+ if (functionage.arity === "statement" && left.identifier) {
+ functionage.name.calls[left.id] = left;
+ }
+ the_paren.expression = [left];
+ if (next_token.id !== ")") {
+ (function next() {
+ let ellipsis;
+ if (next_token.id === "...") {
+ ellipsis = true;
+ advance("...");
}
- }
- if (next_token.id !== ')') {
- (function next() {
- var ellipsis;
- if (next_token.id === '...') {
- if (!option.es6) {
- warn('es6');
- }
- ellipsis = true;
- advance('...');
- }
- the_argument = expression(10);
- if (ellipsis) {
- the_argument.ellipsis = true;
- }
- the_paren.expression.push(the_argument);
- if (next_token.id === ',') {
- advance(',');
- return next();
- }
- }());
- }
- advance(')', the_paren);
- if (the_paren.expression.length === 2) {
- the_paren.free = true;
- if (the_argument.wrapped === true) {
- warn('unexpected_a', the_paren);
+ the_argument = expression(10);
+ if (ellipsis) {
+ the_argument.ellipsis = true;
}
- if (the_argument.id === '(') {
- the_argument.wrapped = true;
+ the_paren.expression.push(the_argument);
+ if (next_token.id === ",") {
+ advance(",");
+ return next();
}
- } else {
- the_paren.free = false;
- }
- return the_paren;
- });
- infix('.', 170, function (left) {
- var the_token = token,
- name = next_token;
- if (
- (left.id !== '(string)' || name.id !== 'indexOf') &&
- (left.id !== '[' || (
- name.id !== 'concat' && name.id !== 'forEach'
- )) &&
- (left.id !== '+' || name.id !== 'slice') &&
- (left.id !== '(regexp)' || (
- name.id !== 'exec' && name.id !== 'test'
- ))
- ) {
- left_check(left, the_token);
+ }());
+ }
+ advance(")", the_paren);
+ if (the_paren.expression.length === 2) {
+ the_paren.free = true;
+ if (the_argument.wrapped === true) {
+ warn("unexpected_a", the_paren);
}
- if (!name.identifier) {
- stop('expected_identifier_a');
+ if (the_argument.id === "(") {
+ the_argument.wrapped = true;
}
- advance();
- survey(name);
+ } else {
+ the_paren.free = false;
+ }
+ return the_paren;
+});
+infix(".", 170, function (left) {
+ const the_token = token;
+ const name = next_token;
+ if (
+ (
+ left.id !== "(string)"
+ || (name.id !== "indexOf" && name.id !== "repeat")
+ )
+ && (
+ left.id !== "["
+ || (
+ name.id !== "concat"
+ && name.id !== "forEach"
+ && name.id !== "join"
+ && name.id !== "map"
+ )
+ )
+ && (left.id !== "+" || name.id !== "slice")
+ && (
+ left.id !== "(regexp)"
+ || (name.id !== "exec" && name.id !== "test")
+ )
+ ) {
+ left_check(left, the_token);
+ }
+ if (!name.identifier) {
+ stop("expected_identifier_a");
+ }
+ advance();
+ survey(name);
// The property name is not an expression.
- the_token.name = name;
- the_token.expression = left;
- return the_token;
- });
- infix('[', 170, function (left) {
- var the_token = token,
- the_subscript = expression(0);
- if (
- the_subscript.id === '(string)' &&
- rx_identifier.test(the_subscript.value)
- ) {
- warn('subscript_a', the_subscript);
- survey(the_subscript);
- } else if (the_subscript.id === '`') {
- warn('unexpected_a', the_subscript);
- }
+ the_token.name = name;
+ the_token.expression = left;
+ return the_token;
+});
+infix("?.", 170, function (left) {
+ const the_token = token;
+ const name = next_token;
+ if (
+ (
+ left.id !== "(string)"
+ || (name.id !== "indexOf" && name.id !== "repeat")
+ )
+ && (
+ left.id !== "["
+ || (
+ name.id !== "concat"
+ && name.id !== "forEach"
+ && name.id !== "join"
+ && name.id !== "map"
+ )
+ )
+ && (left.id !== "+" || name.id !== "slice")
+ && (
+ left.id !== "(regexp)"
+ || (name.id !== "exec" && name.id !== "test")
+ )
+ ) {
left_check(left, the_token);
- the_token.expression = [left, the_subscript];
- advance(']');
- return the_token;
- });
- infix('=>', 170, function (left) {
- return stop('expected_a_before_b', left, '(', artifact(left));
- });
-
- function do_tick() {
- var the_tick = token;
- if (!option.es6) {
- warn('es6', the_tick);
- }
- the_tick.value = [];
- the_tick.expression = [];
- if (next_token.id !== '`') {
- (function part() {
- advance('(string)');
- the_tick.value.push(token);
- if (next_token.id === '${') {
- advance('${');
- the_tick.expression.push(expression(0));
- advance('}');
- return part();
- }
- }());
- }
- advance('`');
- return the_tick;
}
+ if (!name.identifier) {
+ stop("expected_identifier_a");
+ }
+ advance();
+ survey(name);
- infix('`', 160, function (left) {
- var the_tick = do_tick();
- left_check(left, the_tick);
- the_tick.expression = [left].concat(the_tick.expression);
- return the_tick;
- });
+// The property name is not an expression.
- post('++');
- post('--');
- pre('++');
- pre('--');
-
- prefix('+');
- prefix('-');
- prefix('~');
- prefix('!');
- prefix('!!');
- prefix('[', function () {
- var the_token = token;
- the_token.expression = [];
- if (next_token.id !== ']') {
- (function next() {
- var element,
- ellipsis = false;
- if (next_token.id === '...') {
- ellipsis = true;
- if (!option.es6) {
- warn('es6');
- }
- advance('...');
- }
- element = expression(10);
- if (ellipsis) {
- element.ellipsis = true;
- }
- the_token.expression.push(element);
- if (next_token.id === ',') {
- advance(',');
- return next();
- }
- }());
+ the_token.name = name;
+ the_token.expression = left;
+ return the_token;
+});
+infix("[", 170, function (left) {
+ const the_token = token;
+ const the_subscript = expression(0);
+ if (the_subscript.id === "(string)" || the_subscript.id === "`") {
+ const name = survey(the_subscript);
+ if (rx_identifier.test(name)) {
+ warn("subscript_a", the_subscript, name);
}
- advance(']');
- return the_token;
- });
- prefix('/=', function () {
- stop('expected_a_b', token, '/\\=', '/=');
- });
- prefix('=>', function () {
- return stop('expected_a_before_b', token, '()', '=>');
- });
- prefix('new', function () {
- var the_new = token;
- next_token.new = true;
- the_new.expression = expression(150);
- if (the_new.expression.id !== '(') {
- warn('expected_a_before_b', next_token, '()', artifact(next_token));
- }
- return the_new;
- });
- prefix('typeof');
- prefix('void', function () {
- var the_void = token;
- warn('unexpected_a', the_void);
- the_void.expression = expression(0);
- return the_void;
- });
-
- function parameter(list, signature) {
- var ellipsis = false,
- param;
- if (next_token.id === '{') {
- if (!option.es6) {
- warn('es6');
+ }
+ left_check(left, the_token);
+ the_token.expression = [left, the_subscript];
+ advance("]");
+ return the_token;
+});
+infix("=>", 170, function (left) {
+ return stop("wrap_parameter", left);
+});
+
+function do_tick() {
+ const the_tick = token;
+ the_tick.value = [];
+ the_tick.expression = [];
+ if (next_token.id !== "`") {
+ (function part() {
+ advance("(string)");
+ the_tick.value.push(token);
+ if (next_token.id === "${") {
+ advance("${");
+ the_tick.expression.push(expression(0));
+ advance("}");
+ return part();
+ }
+ }());
+ }
+ advance("`");
+ return the_tick;
+}
+
+infix("`", 160, function (left) {
+ const the_tick = do_tick();
+ left_check(left, the_tick);
+ the_tick.expression = [left].concat(the_tick.expression);
+ return the_tick;
+});
+
+post("++");
+post("--");
+pre("++");
+pre("--");
+
+prefix("+");
+prefix("-");
+prefix("~");
+prefix("!");
+prefix("!!");
+prefix("[", function () {
+ const the_token = token;
+ the_token.expression = [];
+ if (next_token.id !== "]") {
+ (function next() {
+ let element;
+ let ellipsis = false;
+ if (next_token.id === "...") {
+ ellipsis = true;
+ advance("...");
+ }
+ element = expression(10);
+ if (ellipsis) {
+ element.ellipsis = true;
+ }
+ the_token.expression.push(element);
+ if (next_token.id === ",") {
+ advance(",");
+ return next();
}
- param = next_token;
- param.names = [];
- advance('{');
- signature.push('{');
- (function subparameter() {
- var subparam = next_token;
- if (!subparam.identifier) {
- return stop('expected_identifier_a');
+ }());
+ }
+ advance("]");
+ return the_token;
+});
+prefix("/=", function () {
+ stop("expected_a_b", token, "/\\=", "/=");
+});
+prefix("=>", function () {
+ return stop("expected_a_before_b", token, "()", "=>");
+});
+prefix("new", function () {
+ const the_new = token;
+ const right = expression(160);
+ if (next_token.id !== "(") {
+ warn("expected_a_before_b", next_token, "()", artifact(next_token));
+ }
+ the_new.expression = right;
+ return the_new;
+});
+prefix("typeof");
+prefix("void", function () {
+ const the_void = token;
+ warn("unexpected_a", the_void);
+ the_void.expression = expression(0);
+ return the_void;
+});
+
+function parameter_list() {
+ const list = [];
+ let optional;
+ const signature = ["("];
+ if (next_token.id !== ")" && next_token.id !== "(end)") {
+ (function parameter() {
+ let ellipsis = false;
+ let param;
+ if (next_token.id === "{") {
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ next_token,
+ next_token.id,
+ optional.id
+ );
}
- survey(subparam);
- advance();
- signature.push(subparam.id);
- if (next_token.id === ':') {
- advance(':');
- advance();
- token.label = subparam;
- subparam = token;
+ param = next_token;
+ param.names = [];
+ advance("{");
+ signature.push("{");
+ (function subparameter() {
+ let subparam = next_token;
if (!subparam.identifier) {
- return stop('expected_identifier_a');
+ return stop("expected_identifier_a");
+ }
+ survey(subparam);
+ advance();
+ signature.push(subparam.id);
+ if (next_token.id === ":") {
+ advance(":");
+ advance();
+ token.label = subparam;
+ subparam = token;
+ if (!subparam.identifier) {
+ return stop("expected_identifier_a");
+ }
+ }
+ if (next_token.id === "=") {
+ advance("=");
+ subparam.expression = expression();
+ param.open = true;
+ }
+ param.names.push(subparam);
+ if (next_token.id === ",") {
+ advance(",");
+ signature.push(", ");
+ return subparameter();
}
+ }());
+ list.push(param);
+ advance("}");
+ signature.push("}");
+ if (next_token.id === ",") {
+ advance(",");
+ signature.push(", ");
+ return parameter();
+ }
+ } else if (next_token.id === "[") {
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ next_token,
+ next_token.id,
+ optional.id
+ );
}
- param.names.push(subparam);
- if (next_token.id === ',') {
- advance(',');
+ param = next_token;
+ param.names = [];
+ advance("[");
+ signature.push("[]");
+ (function subparameter() {
+ const subparam = next_token;
+ if (!subparam.identifier) {
+ return stop("expected_identifier_a");
+ }
+ advance();
+ param.names.push(subparam);
+ if (next_token.id === "=") {
+ advance("=");
+ subparam.expression = expression();
+ param.open = true;
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ return subparameter();
+ }
+ }());
+ list.push(param);
+ advance("]");
+ if (next_token.id === ",") {
+ advance(",");
signature.push(", ");
- return subparameter();
+ return parameter();
}
- }());
- list.push(param);
- advance('}');
- signature.push('}');
- if (next_token.id === ',') {
- advance(',');
- signature.push(", ");
- return parameter(list, signature);
- }
- } else if (next_token.id === '[') {
- if (!option.es6) {
- warn('es6');
- }
- param = next_token;
- param.names = [];
- advance('[');
- signature.push("[]");
- (function subparameter() {
- var subparam = next_token;
- if (!subparam.identifier) {
- return stop('expected_identifier_a');
+ } else {
+ if (next_token.id === "...") {
+ ellipsis = true;
+ signature.push("...");
+ advance("...");
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ next_token,
+ next_token.id,
+ optional.id
+ );
+ }
}
- advance();
- param.names.push(subparam);
- if (next_token.id === ',') {
- advance(',');
- return subparameter();
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a");
}
- }());
- list.push(param);
- advance(']');
- if (next_token.id === ',') {
- advance(',');
- signature.push(", ");
- return parameter(list, signature);
- }
- } else {
- if (next_token.id === '...') {
- if (!option.es6) {
- warn('es6');
- }
- ellipsis = true;
- signature.push("...");
- advance('...');
- }
- if (!next_token.identifier) {
- return stop('expected_identifier_a');
- }
- param = next_token;
- list.push(param);
- advance();
- signature.push(param.id);
- if (ellipsis) {
- param.ellipsis = true;
- } else {
- if (next_token.id === '=') {
- if (!option.es6) {
- warn('es6');
+ param = next_token;
+ list.push(param);
+ advance();
+ signature.push(param.id);
+ if (ellipsis) {
+ param.ellipsis = true;
+ } else {
+ if (next_token.id === "=") {
+ optional = param;
+ advance("=");
+ param.expression = expression(0);
+ } else {
+ if (optional !== undefined) {
+ warn(
+ "required_a_optional_b",
+ param,
+ param.id,
+ optional.id
+ );
+ }
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ signature.push(", ");
+ return parameter();
}
- advance('=');
- param.expression = expression(0);
- }
- if (next_token.id === ',') {
- advance(',');
- signature.push(", ");
- return parameter(list, signature);
}
}
- }
- }
-
- function parameter_list() {
- var list = [], signature = ['('];
- if (next_token.id !== ')' && next_token.id !== '(end)') {
- parameter(list, signature);
- }
- advance(')');
- signature.push(')');
- return [list, signature.join('')];
+ }());
}
+ advance(")");
+ signature.push(")");
+ return [list, signature.join("")];
+}
- function do_function(the_function) {
- var name;
- if (the_function === undefined) {
- the_function = token;
+function do_function(the_function) {
+ let name;
+ if (the_function === undefined) {
+ the_function = token;
// A function statement must have a name that will be in the parent's scope.
- if (the_function.arity === 'statement') {
- if (!next_token.identifier) {
- return stop('expected_identifier_a', next_token);
- }
- name = next_token;
- enroll(name, 'variable', true);
- the_function.name = name;
- name.init = true;
- name.calls = empty();
- advance();
- } else if (name === undefined) {
+ if (the_function.arity === "statement") {
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ name = next_token;
+ enroll(name, "variable", true);
+ the_function.name = name;
+ name.init = true;
+ name.calls = empty();
+ advance();
+ } else if (name === undefined) {
// A function expression may have an optional name.
- if (next_token.identifier) {
- name = next_token;
- the_function.name = name;
- advance();
- } else {
- the_function.name = anon;
- }
+ if (next_token.identifier) {
+ name = next_token;
+ the_function.name = name;
+ advance();
+ } else {
+ the_function.name = anon;
}
- } else {
- name = the_function.name;
- }
- the_function.level = functionage.level + 1;
- if (mega_mode) {
- warn('unexpected_a', the_function);
}
+ } else {
+ name = the_function.name;
+ }
+ the_function.level = functionage.level + 1;
+ if (mega_mode) {
+ warn("unexpected_a", the_function);
+ }
// Don't make functions in loops. It is inefficient, and it can lead to scoping
// errors.
- if (functionage.loop > 0) {
- warn('function_in_loop', the_function);
- }
+ if (functionage.loop > 0) {
+ warn("function_in_loop", the_function);
+ }
// Give the function properties for storing its names and for observing the
// depth of loops and switches.
- the_function.context = empty();
- the_function.loop = 0;
- the_function.switch = 0;
+ the_function.context = empty();
+ the_function.finally = 0;
+ the_function.loop = 0;
+ the_function.switch = 0;
+ the_function.try = 0;
// Push the current function context and establish a new one.
- stack.push(functionage);
- functions.push(the_function);
- functionage = the_function;
- if (the_function.arity !== 'statement' && name) {
- enroll(name, 'function', true);
- name.dead = false;
- name.init = true;
- name.used = 1;
- }
+ stack.push(functionage);
+ functions.push(the_function);
+ functionage = the_function;
+ if (the_function.arity !== "statement" && typeof name === "object") {
+ enroll(name, "function", true);
+ name.dead = false;
+ name.init = true;
+ name.used = 1;
+ }
// Parse the parameter list.
- advance('(');
- token.free = false;
- token.arity = 'function';
- var pl = parameter_list();
- functionage.parameters = pl[0];
- functionage.signature = pl[1];
- functionage.parameters.forEach(function enroll_parameter(name) {
- if (name.identifier) {
- enroll(name, 'parameter', false);
- } else {
- name.names.forEach(enroll_parameter);
- }
- });
+ advance("(");
+ token.free = false;
+ token.arity = "function";
+ [functionage.parameters, functionage.signature] = parameter_list();
+ functionage.parameters.forEach(function enroll_parameter(name) {
+ if (name.identifier) {
+ enroll(name, "parameter", false);
+ } else {
+ name.names.forEach(enroll_parameter);
+ }
+ });
// The function's body is a block.
- the_function.block = block('body');
- if (the_function.arity === 'statement' && next_token.line === token.line) {
- return stop('unexpected_a', next_token);
- }
- if (next_token.id === '.' || next_token.id === '[') {
- warn('unexpected_a');
- }
+ the_function.block = block("body");
+ if (
+ the_function.arity === "statement"
+ && next_token.line === token.line
+ ) {
+ return stop("unexpected_a", next_token);
+ }
+ if (
+ next_token.id === "."
+ || next_token.id === "?."
+ || next_token.id === "["
+ ) {
+ warn("unexpected_a");
+ }
// Restore the previous context.
- functionage = stack.pop();
- return the_function;
+ functionage = stack.pop();
+ return the_function;
+}
+
+prefix("function", do_function);
+
+function fart(pl) {
+ advance("=>");
+ const the_fart = token;
+ the_fart.arity = "binary";
+ the_fart.name = "=>";
+ the_fart.level = functionage.level + 1;
+ functions.push(the_fart);
+ if (functionage.loop > 0) {
+ warn("function_in_loop", the_fart);
}
- prefix('function', do_function);
-
- function fart(pl) {
- if (next_token.id === ';') {
- stop('wrap_assignment', token);
- }
- advance('=>');
- var the_arrow = token;
- the_arrow.arity = 'binary';
- the_arrow.name = "=>";
- the_arrow.level = functionage.level + 1;
- functions.push(the_arrow);
- if (functionage.loop > 0) {
- warn('function_in_loop', the_arrow);
- }
-
// Give the function properties storing its names and for observing the depth
// of loops and switches.
- the_arrow.context = empty();
- the_arrow.loop = 0;
- the_arrow.switch = 0;
+ the_fart.context = empty();
+ the_fart.finally = 0;
+ the_fart.loop = 0;
+ the_fart.switch = 0;
+ the_fart.try = 0;
// Push the current function context and establish a new one.
- stack.push(functionage);
- functionage = the_arrow;
- the_arrow.parameters = pl[0];
- the_arrow.signature = pl[1];
- the_arrow.parameters.forEach(function (name) {
- enroll(name, 'parameter', true);
- });
- if (!option.es6) {
- warn('es6', the_arrow);
- }
- if (next_token.id === '{') {
- warn('expected_a_b', the_arrow, "function", "=>");
- the_arrow.block = block('body');
- } else {
- the_arrow.expression = expression(0);
- }
- functionage = stack.pop();
- return the_arrow;
+ stack.push(functionage);
+ functionage = the_fart;
+ the_fart.parameters = pl[0];
+ the_fart.signature = pl[1];
+ the_fart.parameters.forEach(function (name) {
+ enroll(name, "parameter", true);
+ });
+ if (next_token.id === "{") {
+ warn("expected_a_b", the_fart, "function", "=>");
+ the_fart.block = block("body");
+ } else {
+ the_fart.expression = expression(0);
}
+ functionage = stack.pop();
+ return the_fart;
+}
- prefix('(', function () {
- var the_paren = token,
- the_value,
- cadet = lookahead().id;
+prefix("(", function () {
+ const the_paren = token;
+ let the_value;
+ const cadet = lookahead().id;
// We can distinguish between a parameter list for => and a wrapped expression
// with one token of lookahead.
- if (
- next_token.id === ')' ||
- next_token.id === '...' ||
- (next_token.identifier && (cadet === ',' || cadet === '='))
- ) {
- the_paren.free = false;
- return fart(parameter_list());
- }
- the_paren.free = true;
- the_value = expression(0);
- if (the_value.wrapped === true) {
- warn('unexpected_a', the_paren);
- }
- the_value.wrapped = true;
- advance(')', the_paren);
- if (next_token.id === "=>") {
- if (the_value.arity !== 'variable') {
- return stop('expected_identifier_a', the_value);
+ if (
+ next_token.id === ")"
+ || next_token.id === "..."
+ || (next_token.identifier && (cadet === "," || cadet === "="))
+ ) {
+ the_paren.free = false;
+ return fart(parameter_list());
+ }
+ the_paren.free = true;
+ the_value = expression(0);
+ if (the_value.wrapped === true) {
+ warn("unexpected_a", the_paren);
+ }
+ the_value.wrapped = true;
+ advance(")", the_paren);
+ if (next_token.id === "=>") {
+ if (the_value.arity !== "variable") {
+ if (the_value.id === "{" || the_value.id === "[") {
+ warn("expected_a_before_b", the_paren, "function", "(");
+ return stop("expected_a_b", next_token, "{", "=>");
}
- the_paren.expression = [the_value];
- return fart([the_paren.expression, "(" + the_value.id + ")"]);
+ return stop("expected_identifier_a", the_value);
}
- return the_value;
- });
- prefix('`', do_tick);
- prefix('{', function () {
- var the_brace = token,
- seen = empty();
- the_brace.expression = [];
- if (next_token.id !== '}') {
- (function member() {
- var extra = true,
- id,
- name = next_token,
- value;
+ the_paren.expression = [the_value];
+ return fart([the_paren.expression, "(" + the_value.id + ")"]);
+ }
+ return the_value;
+});
+prefix("`", do_tick);
+prefix("{", function () {
+ const the_brace = token;
+ const seen = empty();
+ the_brace.expression = [];
+ if (next_token.id !== "}") {
+ (function member() {
+ let extra;
+ let full;
+ let id;
+ let name = next_token;
+ let value;
+ advance();
+ if (
+ (name.id === "get" || name.id === "set")
+ && next_token.identifier
+ ) {
+ if (!option.getset) {
+ warn("unexpected_a", name);
+ }
+ extra = name.id;
+ full = extra + " " + next_token.id;
+ name = next_token;
advance();
- if (
- (name.id === 'get' || name.id === 'set') &&
- next_token.identifier
- ) {
- extra = name.id;
- name = next_token;
- advance();
+ id = survey(name);
+ if (seen[full] === true || seen[id] === true) {
+ warn("duplicate_a", name);
}
+ seen[id] = false;
+ seen[full] = true;
+ } else {
id = survey(name);
- if (seen[id] === true) {
- warn('duplicate_a', name);
- } else if (seen[id] === 'get' && extra !== 'set') {
- warn('expected_a_before_b', name, 'set', artifact(name));
+ if (typeof seen[id] === "boolean") {
+ warn("duplicate_a", name);
}
- seen[id] = (extra === 'get')
- ? 'get'
- : true;
- if (name.identifier) {
- switch (next_token.id) {
- case '}':
- case ',':
- if (!option.es6) {
- warn('es6');
- } else if (extra !== true) {
- advance(':');
- }
- value = expression(Infinity, true);
- break;
- case '(':
- if (!option.es6 && typeof extra !== 'string') {
- warn('es6');
- }
- value = do_function({
- arity: 'unary',
- from: name.from,
- id: 'function',
- line: name.line,
- name: name,
- thru: name.from
- }, name);
- break;
- default:
- advance(':');
- value = expression(0);
- }
- value.label = name;
- if (typeof extra === 'string') {
- value.extra = extra;
+ seen[id] = true;
+ }
+ if (name.identifier) {
+ if (next_token.id === "}" || next_token.id === ",") {
+ if (typeof extra === "string") {
+ advance("(");
}
- the_brace.expression.push(value);
+ value = expression(Infinity, true);
+ } else if (next_token.id === "(") {
+ value = do_function({
+ arity: "unary",
+ from: name.from,
+ id: "function",
+ line: name.line,
+ name: (
+ typeof extra === "string"
+ ? extra
+ : id
+ ),
+ thru: name.from
+ });
} else {
- advance(':');
+ if (typeof extra === "string") {
+ advance("(");
+ }
+ let the_colon = next_token;
+ advance(":");
value = expression(0);
- value.label = name;
- the_brace.expression.push(value);
+ if (value.id === name.id) {
+ warn("unexpected_a", the_colon, ": " + name.id);
+ }
}
- if (next_token.id === ',') {
- advance(',');
- return member();
+ value.label = name;
+ if (typeof extra === "string") {
+ value.extra = extra;
}
- }());
- }
- advance('}');
- return the_brace;
- });
-
- stmt(';', function () {
- warn('unexpected_a', token);
- return token;
- });
- stmt('{', function () {
- warn('naked_block', token);
- return block('naked');
- });
- stmt('break', function () {
- var the_break = token,
- the_label;
- if (functionage.loop < 1 && functionage.switch < 1) {
- warn('unexpected_a', the_break);
- }
- the_break.disrupt = true;
- if (next_token.identifier && token.line === next_token.line) {
- the_label = functionage.context[next_token.id];
- if (
- the_label === undefined ||
- the_label.role !== 'label' ||
- the_label.dead
- ) {
- warn((the_label !== undefined && the_label.dead)
- ? 'out_of_scope_a'
- : 'not_label_a');
+ the_brace.expression.push(value);
} else {
- the_label.used += 1;
+ advance(":");
+ value = expression(0);
+ value.label = name;
+ the_brace.expression.push(value);
}
- the_break.label = next_token;
- advance();
+ if (next_token.id === ",") {
+ advance(",");
+ return member();
+ }
+ }());
+ }
+ advance("}");
+ return the_brace;
+});
+
+stmt(";", function () {
+ warn("unexpected_a", token);
+ return token;
+});
+stmt("{", function () {
+ warn("naked_block", token);
+ return block("naked");
+});
+stmt("break", function () {
+ const the_break = token;
+ let the_label;
+ if (
+ (functionage.loop < 1 && functionage.switch < 1)
+ || functionage.finally > 0
+ ) {
+ warn("unexpected_a", the_break);
+ }
+ the_break.disrupt = true;
+ if (next_token.identifier && token.line === next_token.line) {
+ the_label = functionage.context[next_token.id];
+ if (
+ the_label === undefined
+ || the_label.role !== "label"
+ || the_label.dead
+ ) {
+ warn(
+ (the_label !== undefined && the_label.dead)
+ ? "out_of_scope_a"
+ : "not_label_a"
+ );
+ } else {
+ the_label.used += 1;
}
- advance(';');
- return the_break;
- });
+ the_break.label = next_token;
+ advance();
+ }
+ advance(";");
+ return the_break;
+});
- function do_var() {
- var the_statement = token,
- is_const = the_statement.id === 'const';
- the_statement.names = [];
+function do_var() {
+ const the_statement = token;
+ const is_const = the_statement.id === "const";
+ the_statement.names = [];
-// A program may use var or let, but not both, and let and const require
-// option.es6.
+// A program may use var or let, but not both.
- if (is_const) {
- if (!option.es6) {
- warn('es6', the_statement);
- }
- } else if (var_mode === undefined) {
+ if (!is_const) {
+ if (var_mode === undefined) {
var_mode = the_statement.id;
- if (!option.es6 && var_mode !== 'var') {
- warn('es6', the_statement);
- }
} else if (the_statement.id !== var_mode) {
warn(
- 'expected_a_b',
+ "expected_a_b",
the_statement,
var_mode,
the_statement.id
);
}
+ }
// We don't expect to see variables created in switch statements.
- if (functionage.switch > 0) {
- warn('var_switch', the_statement);
- }
- if (functionage.loop > 0 && the_statement.id === 'var') {
- warn('var_loop', the_statement);
- }
- (function next() {
- if (next_token.id === '{' && the_statement.id !== 'var') {
- var the_brace = next_token;
- the_brace.names = [];
- advance('{');
- (function pair() {
+ if (functionage.switch > 0) {
+ warn("var_switch", the_statement);
+ }
+ if (functionage.loop > 0 && the_statement.id === "var") {
+ warn("var_loop", the_statement);
+ }
+ (function next() {
+ if (next_token.id === "{" && the_statement.id !== "var") {
+ const the_brace = next_token;
+ advance("{");
+ (function pair() {
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ const name = next_token;
+ survey(name);
+ advance();
+ if (next_token.id === ":") {
+ advance(":");
if (!next_token.identifier) {
- return stop('expected_identifier_a', next_token);
+ return stop("expected_identifier_a", next_token);
}
- var name = next_token;
- survey(name);
+ next_token.label = name;
+ the_statement.names.push(next_token);
+ enroll(next_token, "variable", is_const);
advance();
- if (next_token.id === ':') {
- advance(':');
- if (!next_token.identifier) {
- return stop('expected_identifier_a', next_token);
- }
- next_token.label = name;
- the_brace.names.push(next_token);
- enroll(next_token, 'variable', is_const);
- advance();
- } else {
- the_brace.names.push(name);
- enroll(name, 'variable', is_const);
- }
- if (next_token.id === ',') {
- advance(',');
- return pair();
- }
- }());
- advance('}');
- advance('=');
- the_brace.expression = expression(0);
- the_statement.names.push(the_brace);
- } else if (next_token.id === '[' && the_statement.id !== 'var') {
- var the_bracket = next_token;
- the_bracket.names = [];
- advance('[');
- (function element() {
- var ellipsis;
- if (next_token.id === '...') {
- ellipsis = true;
- advance('...');
- }
- if (!next_token.identifier) {
- return stop('expected_identifier_a', next_token);
+ the_brace.open = true;
+ } else {
+ the_statement.names.push(name);
+ enroll(name, "variable", is_const);
+ }
+ name.dead = false;
+ name.init = true;
+ if (next_token.id === "=") {
+ advance("=");
+ name.expression = expression();
+ the_brace.open = true;
+ }
+ if (next_token.id === ",") {
+ advance(",");
+ return pair();
+ }
+ }());
+ advance("}");
+ advance("=");
+ the_statement.expression = expression(0);
+ } else if (next_token.id === "[" && the_statement.id !== "var") {
+ const the_bracket = next_token;
+ advance("[");
+ (function element() {
+ let ellipsis;
+ if (next_token.id === "...") {
+ ellipsis = true;
+ advance("...");
+ }
+ if (!next_token.identifier) {
+ return stop("expected_identifier_a", next_token);
+ }
+ const name = next_token;
+ advance();
+ the_statement.names.push(name);
+ enroll(name, "variable", is_const);
+ name.dead = false;
+ name.init = true;
+ if (ellipsis) {
+ name.ellipsis = true;
+ } else {
+ if (next_token.id === "=") {
+ advance("=");
+ name.expression = expression();
+ the_bracket.open = true;
}
- var name = next_token;
- advance();
- the_bracket.names.push(name);
- enroll(name, 'variable', the_statement.id === 'const');
- if (ellipsis) {
- name.ellipsis = true;
- } else if (next_token.id === ',') {
- advance(',');
+ if (next_token.id === ",") {
+ advance(",");
return element();
}
- }());
- advance(']');
- advance('=');
- the_bracket.expression = expression(0);
- the_statement.names.push(the_bracket);
- } else if (next_token.identifier) {
- var name = next_token;
- advance();
- if (name.id === 'ignore') {
- warn('unexpected_a', name);
}
- enroll(name, 'variable', is_const);
- if (next_token.id === '=' || is_const) {
- advance('=');
- name.expression = expression(0);
- name.init = true;
- }
- the_statement.names.push(name);
- } else {
- return stop('expected_identifier_a', next_token);
+ }());
+ advance("]");
+ advance("=");
+ the_statement.expression = expression(0);
+ } else if (next_token.identifier) {
+ const name = next_token;
+ advance();
+ if (name.id === "ignore") {
+ warn("unexpected_a", name);
}
- if (next_token.id === ',') {
- advance(',');
- return next();
+ enroll(name, "variable", is_const);
+ if (next_token.id === "=" || is_const) {
+ advance("=");
+ name.dead = false;
+ name.init = true;
+ name.expression = expression(0);
}
- }());
- the_statement.open =
- the_statement.names.length > 1 &&
- the_statement.line !== the_statement.names[1].line;
- semicolon();
- return the_statement;
+ the_statement.names.push(name);
+ } else {
+ return stop("expected_identifier_a", next_token);
+ }
+ }());
+ semicolon();
+ return the_statement;
+}
+
+stmt("const", do_var);
+stmt("continue", function () {
+ const the_continue = token;
+ if (functionage.loop < 1 || functionage.finally > 0) {
+ warn("unexpected_a", the_continue);
}
-
- stmt('const', do_var);
- stmt('continue', function () {
- var the_continue = token;
- if (functionage.loop < 1) {
- warn('unexpected_a', the_continue);
- }
- not_top_level(the_continue);
- the_continue.disrupt = true;
- warn('unexpected_a', the_continue);
- advance(';');
- return the_continue;
- });
- stmt('debugger', function () {
- var the_debug = token;
- if (!option.devel) {
- warn('unexpected_a', the_debug);
- }
- semicolon();
- return the_debug;
- });
- stmt('delete', function () {
- var the_token = token,
- the_value = expression(0);
- if (
- (the_value.id !== '.' && the_value.id !== '[') ||
- the_value.arity !== 'binary'
- ) {
- stop('expected_a_b', the_value, '.', artifact(the_value));
- }
- the_token.expression = the_value;
- semicolon();
- return the_token;
- });
- stmt('do', function () {
- var the_do = token;
- not_top_level(the_do);
- functionage.loop += 1;
- the_do.block = block();
- advance('while');
- the_do.expression = condition();
- semicolon();
- if (the_do.block.disrupt === true) {
- warn('weird_loop', the_do);
- }
- functionage.loop -= 1;
- return the_do;
- });
- stmt('export', function () {
- var the_export = token;
- if (!option.es6) {
- warn('es6', the_export);
- }
- if (typeof module_mode === 'object') {
- warn('unexpected_directive_a', module_mode, module_mode.directive);
+ not_top_level(the_continue);
+ the_continue.disrupt = true;
+ warn("unexpected_a", the_continue);
+ advance(";");
+ return the_continue;
+});
+stmt("debugger", function () {
+ const the_debug = token;
+ if (!option.devel) {
+ warn("unexpected_a", the_debug);
+ }
+ semicolon();
+ return the_debug;
+});
+stmt("delete", function () {
+ const the_token = token;
+ const the_value = expression(0);
+ if (
+ (the_value.id !== "." && the_value.id !== "[")
+ || the_value.arity !== "binary"
+ ) {
+ stop("expected_a_b", the_value, ".", artifact(the_value));
+ }
+ the_token.expression = the_value;
+ semicolon();
+ return the_token;
+});
+stmt("do", function () {
+ const the_do = token;
+ not_top_level(the_do);
+ functionage.loop += 1;
+ the_do.block = block();
+ advance("while");
+ the_do.expression = condition();
+ semicolon();
+ if (the_do.block.disrupt === true) {
+ warn("weird_loop", the_do);
+ }
+ functionage.loop -= 1;
+ return the_do;
+});
+stmt("export", function () {
+ const the_export = token;
+ let the_id;
+ let the_name;
+ let the_thing;
+
+ function export_id() {
+ if (!next_token.identifier) {
+ stop("expected_identifier_a");
}
- advance('default');
- if (export_mode) {
- warn('duplicate_a', token);
+ the_id = next_token.id;
+ the_name = global.context[the_id];
+ if (the_name === undefined) {
+ warn("unexpected_a");
+ } else {
+ the_name.used += 1;
+ if (exports[the_id] !== undefined) {
+ warn("duplicate_a");
+ }
+ exports[the_id] = the_name;
}
- module_mode = true;
- export_mode = true;
- the_export.expression = expression(0);
- semicolon();
- return the_export;
- });
- stmt('for', function () {
- var first,
- the_for = token;
- if (!option.for) {
- warn('unexpected_a', the_for);
- }
- not_top_level(the_for);
- functionage.loop += 1;
- advance('(');
- token.free = true;
- if (next_token.id === ';') {
- return stop('expected_a_b', the_for, 'while (', 'for (;');
+ advance();
+ the_export.expression.push(the_thing);
+ }
+
+ the_export.expression = [];
+ if (next_token.id === "default") {
+ if (exports.default !== undefined) {
+ warn("duplicate_a");
}
+ advance("default");
+ the_thing = expression(0);
if (
- next_token.id === 'var' ||
- next_token.id === 'let' ||
- next_token.id === 'const'
+ the_thing.id !== "("
+ || the_thing.expression[0].id !== "."
+ || the_thing.expression[0].expression.id !== "Object"
+ || the_thing.expression[0].name.id !== "freeze"
) {
- return stop('unexpected_a');
+ warn("freeze_exports", the_thing);
+ }
+ if (next_token.id === ";") {
+ semicolon();
}
- first = expression(0);
- if (first.id === 'in') {
- if (first.expression[0].arity !== 'variable') {
- warn('bad_assignment_a', first.expression[0]);
+ exports.default = the_thing;
+ the_export.expression.push(the_thing);
+ } else {
+ if (next_token.id === "function") {
+ warn("freeze_exports");
+ the_thing = statement();
+ the_name = the_thing.name;
+ the_id = the_name.id;
+ the_name.used += 1;
+ if (exports[the_id] !== undefined) {
+ warn("duplicate_a", the_name);
}
- the_for.name = first.expression[0];
- the_for.expression = first.expression[1];
- warn('expected_a_b', the_for, 'Object.keys', 'for in');
+ exports[the_id] = the_thing;
+ the_export.expression.push(the_thing);
+ the_thing.statement = false;
+ the_thing.arity = "unary";
+ } else if (
+ next_token.id === "var"
+ || next_token.id === "let"
+ || next_token.id === "const"
+ ) {
+ warn("unexpected_a", next_token);
+ statement();
+ } else if (next_token.id === "{") {
+ advance("{");
+ (function loop() {
+ export_id();
+ if (next_token.id === ",") {
+ advance(",");
+ return loop();
+ }
+ }());
+ advance("}");
+ semicolon();
} else {
- the_for.initial = first;
- advance(';');
- the_for.expression = expression(0);
- advance(';');
- the_for.inc = expression(0);
- if (the_for.inc.id === '++') {
- warn('expected_a_b', the_for.inc, '+= 1', '++');
- }
+ stop("unexpected_a");
}
- advance(')');
- the_for.block = block();
- if (the_for.block.disrupt === true) {
- warn('weird_loop', the_for);
+ }
+ module_mode = true;
+ return the_export;
+});
+stmt("for", function () {
+ let first;
+ const the_for = token;
+ if (!option.for) {
+ warn("unexpected_a", the_for);
+ }
+ not_top_level(the_for);
+ functionage.loop += 1;
+ advance("(");
+ token.free = true;
+ if (next_token.id === ";") {
+ return stop("expected_a_b", the_for, "while (", "for (;");
+ }
+ if (
+ next_token.id === "var"
+ || next_token.id === "let"
+ || next_token.id === "const"
+ ) {
+ return stop("unexpected_a");
+ }
+ first = expression(0);
+ if (first.id === "in") {
+ if (first.expression[0].arity !== "variable") {
+ warn("bad_assignment_a", first.expression[0]);
+ }
+ the_for.name = first.expression[0];
+ the_for.expression = first.expression[1];
+ warn("expected_a_b", the_for, "Object.keys", "for in");
+ } else {
+ the_for.initial = first;
+ advance(";");
+ the_for.expression = expression(0);
+ advance(";");
+ the_for.inc = expression(0);
+ if (the_for.inc.id === "++") {
+ warn("expected_a_b", the_for.inc, "+= 1", "++");
}
- functionage.loop -= 1;
- return the_for;
- });
- stmt('function', do_function);
- stmt('if', function () {
- var the_else,
- the_if = token;
- the_if.expression = condition();
- the_if.block = block();
- if (next_token.id === 'else') {
- advance('else');
- the_else = token;
- the_if.else = (next_token.id === 'if')
- ? statement()
- : block();
- if (the_if.block.disrupt === true) {
- if (the_if.else.disrupt === true) {
- the_if.disrupt = true;
- } else {
- warn('unexpected_a', the_else);
- }
+ }
+ advance(")");
+ the_for.block = block();
+ if (the_for.block.disrupt === true) {
+ warn("weird_loop", the_for);
+ }
+ functionage.loop -= 1;
+ return the_for;
+});
+stmt("function", do_function);
+stmt("if", function () {
+ let the_else;
+ const the_if = token;
+ the_if.expression = condition();
+ the_if.block = block();
+ if (next_token.id === "else") {
+ advance("else");
+ the_else = token;
+ the_if.else = (
+ next_token.id === "if"
+ ? statement()
+ : block()
+ );
+ if (the_if.block.disrupt === true) {
+ if (the_if.else.disrupt === true) {
+ the_if.disrupt = true;
+ } else {
+ warn("unexpected_a", the_else);
}
}
- return the_if;
- });
- stmt('import', function () {
- var the_import = token;
- if (!option.es6) {
- warn('es6', the_import);
- } else if (typeof module_mode === 'object') {
- warn('unexpected_directive_a', module_mode, module_mode.directive);
- }
- module_mode = true;
- if (!next_token.identifier) {
- return stop('expected_identifier_a');
- }
- var name = next_token;
+ }
+ return the_if;
+});
+stmt("import", function () {
+ const the_import = token;
+ let name;
+ if (typeof module_mode === "object") {
+ warn("unexpected_directive_a", module_mode, module_mode.directive);
+ }
+ module_mode = true;
+ if (next_token.identifier) {
+ name = next_token;
advance();
- if (name.id === 'ignore') {
- warn('unexpected_a', name);
+ if (name.id === "ignore") {
+ warn("unexpected_a", name);
}
- enroll(name, 'variable', true);
- advance('from');
- advance('(string)');
- the_import.import = token;
+ enroll(name, "variable", true);
the_import.name = name;
- if (!rx_identifier.test(token.value)) {
- warn('bad_module_name_a', token);
- }
- imports.push(token.value);
- semicolon();
- return the_import;
- });
- stmt('let', do_var);
- stmt('return', function () {
- var the_return = token;
- not_top_level(the_return);
- the_return.disrupt = true;
- if (next_token.id !== ';' && the_return.line === next_token.line) {
- the_return.expression = expression(10);
- }
- advance(';');
- return the_return;
- });
- stmt('switch', function () {
- var dups = [],
- last,
- stmts,
- the_cases = [],
- the_disrupt = true,
- the_switch = token;
- not_top_level(the_switch);
- functionage.switch += 1;
- advance('(');
- token.free = true;
- the_switch.expression = expression(0);
- the_switch.block = the_cases;
- advance(')');
- advance('{');
- (function major() {
- var the_case = next_token;
- the_case.arity = 'statement';
- the_case.expression = [];
- (function minor() {
- advance('case');
- token.switch = true;
- var exp = expression(0);
- if (dups.some(function (thing) {
- return are_similar(thing, exp);
- })) {
- warn('unexpected_a', exp);
+ } else {
+ const names = [];
+ advance("{");
+ if (next_token.id !== "}") {
+ while (true) {
+ if (!next_token.identifier) {
+ stop("expected_identifier_a");
}
- dups.push(exp);
- the_case.expression.push(exp);
- advance(':');
- if (next_token.id === 'case') {
- return minor();
+ name = next_token;
+ advance();
+ if (name.id === "ignore") {
+ warn("unexpected_a", name);
}
- }());
- stmts = statements();
- if (stmts.length < 1) {
- warn('expected_statements_a');
- return;
- }
- the_case.block = stmts;
- the_cases.push(the_case);
- last = stmts[stmts.length - 1];
- if (last.disrupt) {
- if (last.id === 'break' && last.label === undefined) {
- the_disrupt = false;
+ enroll(name, "variable", true);
+ names.push(name);
+ if (next_token.id !== ",") {
+ break;
}
- } else {
- warn(
- 'expected_a_before_b',
- next_token,
- 'break;',
- artifact(next_token)
- );
+ advance(",");
}
- if (next_token.id === 'case') {
- return major();
+ }
+ advance("}");
+ the_import.name = names;
+ }
+ advance("from");
+ advance("(string)");
+ the_import.import = token;
+ if (!rx_module.test(token.value)) {
+ warn("bad_module_name_a", token);
+ }
+ froms.push(token.value);
+ semicolon();
+ return the_import;
+});
+stmt("let", do_var);
+stmt("return", function () {
+ const the_return = token;
+ not_top_level(the_return);
+ if (functionage.finally > 0) {
+ warn("unexpected_a", the_return);
+ }
+ the_return.disrupt = true;
+ if (next_token.id !== ";" && the_return.line === next_token.line) {
+ the_return.expression = expression(10);
+ }
+ advance(";");
+ return the_return;
+});
+stmt("switch", function () {
+ let dups = [];
+ let last;
+ let stmts;
+ const the_cases = [];
+ let the_disrupt = true;
+ const the_switch = token;
+ not_top_level(the_switch);
+ if (functionage.finally > 0) {
+ warn("unexpected_a", the_switch);
+ }
+ functionage.switch += 1;
+ advance("(");
+ token.free = true;
+ the_switch.expression = expression(0);
+ the_switch.block = the_cases;
+ advance(")");
+ advance("{");
+ (function major() {
+ const the_case = next_token;
+ the_case.arity = "statement";
+ the_case.expression = [];
+ (function minor() {
+ advance("case");
+ token.switch = true;
+ const exp = expression(0);
+ if (dups.some(function (thing) {
+ return are_similar(thing, exp);
+ })) {
+ warn("unexpected_a", exp);
+ }
+ dups.push(exp);
+ the_case.expression.push(exp);
+ advance(":");
+ if (next_token.id === "case") {
+ return minor();
}
}());
- dups = undefined;
- if (next_token.id === 'default') {
- advance('default');
- token.switch = true;
- advance(':');
- the_switch.else = statements();
- if (the_switch.else.length < 1) {
- warn('expected_statements_a');
+ stmts = statements();
+ if (stmts.length < 1) {
+ warn("expected_statements_a");
+ return;
+ }
+ the_case.block = stmts;
+ the_cases.push(the_case);
+ last = stmts[stmts.length - 1];
+ if (last.disrupt) {
+ if (last.id === "break" && last.label === undefined) {
the_disrupt = false;
- } else {
- the_disrupt =
- the_disrupt &&
- the_switch.else[the_switch.else.length - 1].disrupt;
}
} else {
+ warn(
+ "expected_a_before_b",
+ next_token,
+ "break;",
+ artifact(next_token)
+ );
+ }
+ if (next_token.id === "case") {
+ return major();
+ }
+ }());
+ dups = undefined;
+ if (next_token.id === "default") {
+ const the_default = next_token;
+ advance("default");
+ token.switch = true;
+ advance(":");
+ the_switch.else = statements();
+ if (the_switch.else.length < 1) {
+ warn("unexpected_a", the_default);
the_disrupt = false;
+ } else {
+ const the_last = the_switch.else[the_switch.else.length - 1];
+ if (the_last.id === "break" && the_last.label === undefined) {
+ warn("unexpected_a", the_last);
+ the_last.disrupt = false;
+ }
+ the_disrupt = the_disrupt && the_last.disrupt;
}
- advance('}', the_switch);
- functionage.switch -= 1;
- the_switch.disrupt = the_disrupt;
- return the_switch;
- });
- stmt('throw', function () {
- var the_throw = token;
- the_throw.disrupt = true;
- the_throw.expression = expression(10);
- semicolon();
- return the_throw;
- });
- stmt('try', function () {
- var clause = false,
- the_catch,
- the_disrupt,
- the_try = token;
- the_try.block = block();
- the_disrupt = the_try.block.disrupt;
- if (next_token.id === 'catch') {
- var ignored = 'ignore';
- clause = true;
- the_catch = next_token;
- the_try.catch = the_catch;
- advance('catch');
- advance('(');
+ } else {
+ the_disrupt = false;
+ }
+ advance("}", the_switch);
+ functionage.switch -= 1;
+ the_switch.disrupt = the_disrupt;
+ return the_switch;
+});
+stmt("throw", function () {
+ const the_throw = token;
+ the_throw.disrupt = true;
+ the_throw.expression = expression(10);
+ semicolon();
+ if (functionage.try > 0) {
+ warn("unexpected_a", the_throw);
+ }
+ return the_throw;
+});
+stmt("try", function () {
+ let the_catch;
+ let the_disrupt;
+ const the_try = token;
+ if (functionage.try > 0) {
+ warn("unexpected_a", the_try);
+ }
+ functionage.try += 1;
+ the_try.block = block();
+ the_disrupt = the_try.block.disrupt;
+ if (next_token.id === "catch") {
+ let ignored = "ignore";
+ the_catch = next_token;
+ the_try.catch = the_catch;
+ advance("catch");
+ if (next_token.id === "(") {
+ advance("(");
if (!next_token.identifier) {
- return stop('expected_identifier_a', next_token);
+ return stop("expected_identifier_a", next_token);
}
- if (next_token.id !== 'ignore') {
+ if (next_token.id !== "ignore") {
ignored = undefined;
the_catch.name = next_token;
- enroll(next_token, 'exception', true);
+ enroll(next_token, "exception", true);
}
advance();
- advance(')');
- the_catch.block = block(ignored);
- if (the_catch.block.disrupt !== true) {
- the_disrupt = false;
- }
- }
- if (next_token.id === 'finally') {
- clause = true;
- advance('finally');
- the_try.else = block();
- the_disrupt = the_try.else.disrupt;
+ advance(")");
}
- the_try.disrupt = the_disrupt;
- if (!clause) {
- warn('expected_a_before_b', next_token, 'catch', artifact(next_token));
+ the_catch.block = block(ignored);
+ if (the_catch.block.disrupt !== true) {
+ the_disrupt = false;
}
- return the_try;
- });
- stmt('var', do_var);
- stmt('while', function () {
- var the_while = token;
- not_top_level(the_while);
- functionage.loop += 1;
- the_while.expression = condition();
- the_while.block = block();
- if (the_while.block.disrupt === true) {
- warn('weird_loop', the_while);
- }
- functionage.loop -= 1;
- return the_while;
- });
- stmt('with', function () {
- stop('unexpected_a', token);
- });
+ } else {
+ warn(
+ "expected_a_before_b",
+ next_token,
+ "catch",
+ artifact(next_token)
+ );
- ternary('?', ':');
+ }
+ if (next_token.id === "finally") {
+ functionage.finally += 1;
+ advance("finally");
+ the_try.else = block();
+ the_disrupt = the_try.else.disrupt;
+ functionage.finally -= 1;
+ }
+ the_try.disrupt = the_disrupt;
+ functionage.try -= 1;
+ return the_try;
+});
+stmt("var", do_var);
+stmt("while", function () {
+ const the_while = token;
+ not_top_level(the_while);
+ functionage.loop += 1;
+ the_while.expression = condition();
+ the_while.block = block();
+ if (the_while.block.disrupt === true) {
+ warn("weird_loop", the_while);
+ }
+ functionage.loop -= 1;
+ return the_while;
+});
+stmt("with", function () {
+ stop("unexpected_a", token);
+});
+
+ternary("?", ":");
// Ambulation of the parse tree.
- function action(when) {
+function action(when) {
// Produce a function that will register task functions that will be called as
// the tree is traversed.
- return function (arity, id, task) {
- var a_set = when[arity],
- i_set;
+ return function (arity, id, task) {
+ let a_set = when[arity];
+ let i_set;
// The id parameter is optional. If excluded, the task will be applied to all
// ids.
- if (typeof id !== 'string') {
- task = id;
- id = '(all)';
- }
+ if (typeof id !== "string") {
+ task = id;
+ id = "(all)";
+ }
// If this arity has no registrations yet, then create a set object to hold
// them.
- if (a_set === undefined) {
- a_set = empty();
- when[arity] = a_set;
- }
+ if (a_set === undefined) {
+ a_set = empty();
+ when[arity] = a_set;
+ }
// If this id has no registrations yet, then create a set array to hold them.
- i_set = a_set[id];
- if (i_set === undefined) {
- i_set = [];
- a_set[id] = i_set;
- }
+ i_set = a_set[id];
+ if (i_set === undefined) {
+ i_set = [];
+ a_set[id] = i_set;
+ }
// Register the task with the arity and the id.
- i_set.push(task);
- };
- }
+ i_set.push(task);
+ };
+}
- function amble(when) {
+function amble(when) {
// Produce a function that will act on the tasks registered by an action
// function while walking the tree.
- return function (the_token) {
+ return function (the_token) {
// Given a task set that was built by an action function, run all of the
// relevant tasks on the token.
- var a_set = when[the_token.arity],
- i_set;
+ let a_set = when[the_token.arity];
+ let i_set;
// If there are tasks associated with the token's arity...
- if (a_set !== undefined) {
+ if (a_set !== undefined) {
// If there are tasks associated with the token's id...
- i_set = a_set[the_token.id];
- if (i_set !== undefined) {
- i_set.forEach(function (task) {
- return task(the_token);
- });
- }
+ i_set = a_set[the_token.id];
+ if (i_set !== undefined) {
+ i_set.forEach(function (task) {
+ return task(the_token);
+ });
+ }
// If there are tasks for all ids.
- i_set = a_set['(all)'];
- if (i_set !== undefined) {
- i_set.forEach(function (task) {
- return task(the_token);
- });
- }
+ i_set = a_set["(all)"];
+ if (i_set !== undefined) {
+ i_set.forEach(function (task) {
+ return task(the_token);
+ });
}
- };
- }
-
- var posts = empty(),
- pres = empty(),
- preaction = action(pres),
- postaction = action(posts),
- preamble = amble(pres),
- postamble = amble(posts);
-
- function walk_expression(thing) {
- if (thing) {
- if (Array.isArray(thing)) {
- thing.forEach(walk_expression);
- } else {
- preamble(thing);
- walk_expression(thing.expression);
- if (thing.id === 'function') {
- walk_statement(thing.block);
- }
- switch (thing.arity) {
- case 'post':
- case 'pre':
- warn('unexpected_a', thing);
- break;
- case 'statement':
- case 'assignment':
- warn('unexpected_statement_a', thing);
- break;
- }
- postamble(thing);
+ }
+ };
+}
+
+const posts = empty();
+const pres = empty();
+const preaction = action(pres);
+const postaction = action(posts);
+const preamble = amble(pres);
+const postamble = amble(posts);
+
+function walk_expression(thing) {
+ if (thing) {
+ if (Array.isArray(thing)) {
+ thing.forEach(walk_expression);
+ } else {
+ preamble(thing);
+ walk_expression(thing.expression);
+ if (thing.id === "function") {
+ walk_statement(thing.block);
+ }
+ if (thing.arity === "pre" || thing.arity === "post") {
+ warn("unexpected_a", thing);
+ } else if (
+ thing.arity === "statement"
+ || thing.arity === "assignment"
+ ) {
+ warn("unexpected_statement_a", thing);
}
+ postamble(thing);
}
}
+}
- function walk_statement(thing) {
- if (thing) {
- if (Array.isArray(thing)) {
- thing.forEach(walk_statement);
- } else {
- preamble(thing);
- walk_expression(thing.expression);
- switch (thing.arity) {
- case 'statement':
- case 'assignment':
- break;
- case 'binary':
- if (thing.id !== '(') {
- warn('unexpected_expression_a', thing);
- }
- break;
- default:
- warn('unexpected_expression_a', thing);
+function walk_statement(thing) {
+ if (thing) {
+ if (Array.isArray(thing)) {
+ thing.forEach(walk_statement);
+ } else {
+ preamble(thing);
+ walk_expression(thing.expression);
+ if (thing.arity === "binary") {
+ if (thing.id !== "(") {
+ warn("unexpected_expression_a", thing);
}
- walk_statement(thing.block);
- walk_statement(thing.else);
- postamble(thing);
+ } else if (
+ thing.arity !== "statement"
+ && thing.arity !== "assignment"
+ ) {
+ warn("unexpected_expression_a", thing);
}
+ walk_statement(thing.block);
+ walk_statement(thing.else);
+ postamble(thing);
}
}
+}
- function lookup(thing) {
- if (thing.arity === 'variable') {
+function lookup(thing) {
+ if (thing.arity === "variable") {
// Look up the variable in the current context.
- var the_variable = functionage.context[thing.id];
+ let the_variable = functionage.context[thing.id];
// If it isn't local, search all the other contexts. If there are name
// collisions, take the most recent.
- if (the_variable === undefined) {
- stack.forEach(function (outer) {
- var a_variable = outer.context[thing.id];
- if (
- a_variable !== undefined &&
- a_variable.role !== 'label'
- ) {
- the_variable = a_variable;
- }
- });
+ if (the_variable === undefined) {
+ stack.forEach(function (outer) {
+ const a_variable = outer.context[thing.id];
+ if (
+ a_variable !== undefined
+ && a_variable.role !== "label"
+ ) {
+ the_variable = a_variable;
+ }
+ });
// If it isn't in any of those either, perhaps it is a predefined global.
// If so, add it to the global context.
- if (the_variable === undefined) {
- if (declared_globals[thing.id] === undefined) {
- warn('undeclared_a', thing);
- return;
- }
- the_variable = {
- dead: false,
- function: global,
- id: thing.id,
- init: true,
- role: 'variable',
- used: 0,
- writable: false
- };
- global.context[thing.id] = the_variable;
+ if (the_variable === undefined) {
+ if (declared_globals[thing.id] === undefined) {
+ warn("undeclared_a", thing);
+ return;
}
- the_variable.closure = true;
- functionage.context[thing.id] = the_variable;
- } else if (the_variable.role === 'label') {
- warn('label_a', thing);
+ the_variable = {
+ dead: false,
+ parent: global,
+ id: thing.id,
+ init: true,
+ role: "variable",
+ used: 0,
+ writable: false
+ };
+ global.context[thing.id] = the_variable;
}
- if (the_variable.dead) {
- warn('out_of_scope_a', thing);
- }
- return the_variable;
- }
- }
-
- function subactivate(name) {
- name.init = true;
- name.dead = false;
- blockage.live.push(name);
- }
-
- function preaction_function(thing) {
- if (thing.arity === 'statement' && blockage.body !== true) {
- warn('unexpected_a', thing);
- }
- stack.push(functionage);
- block_stack.push(blockage);
- functionage = thing;
- blockage = thing;
- thing.live = [];
- if (typeof thing.name === 'object') {
- thing.name.dead = false;
- thing.name.init = true;
- }
- switch (thing.extra) {
- case 'get':
- if (thing.parameters.length !== 0) {
- warn('bad_get', thing);
- }
- break;
- case 'set':
- if (thing.parameters.length !== 1) {
- warn('bad_set', thing);
- }
- break;
- }
- thing.parameters.forEach(function (name) {
- walk_expression(name.expression);
- if (name.id === '{' || name.id === '[') {
- name.names.forEach(subactivate);
- } else {
- name.dead = false;
- name.init = true;
- }
- });
- }
-
- function bitwise_check(thing) {
- if (!option.bitwise && bitwiseop[thing.id] === true) {
- warn('unexpected_a', thing);
+ the_variable.closure = true;
+ functionage.context[thing.id] = the_variable;
+ } else if (the_variable.role === "label") {
+ warn("label_a", thing);
}
if (
- thing.id !== '(' &&
- thing.id !== '&&' &&
- thing.id !== '||' &&
- thing.id !== '=' &&
- Array.isArray(thing.expression) &&
- thing.expression.length === 2 && (
- relationop[thing.expression[0].id] === true ||
- relationop[thing.expression[1].id] === true
+ the_variable.dead
+ && (
+ the_variable.calls === undefined
+ || the_variable.calls[functionage.name.id] === undefined
)
) {
- warn('unexpected_a', thing);
+ warn("out_of_scope_a", thing);
}
+ return the_variable;
}
+}
- function pop_block() {
- blockage.live.forEach(function (name) {
- name.dead = true;
- });
- delete blockage.live;
- blockage = block_stack.pop();
- }
+function subactivate(name) {
+ name.init = true;
+ name.dead = false;
+ blockage.live.push(name);
+}
- function activate(name) {
- if (name.expression !== undefined) {
- walk_expression(name.expression);
- if (name.id === '{' || name.id === '[') {
- name.names.forEach(subactivate);
- } else {
- name.init = true;
- }
+function preaction_function(thing) {
+ if (thing.arity === "statement" && blockage.body !== true) {
+ warn("unexpected_a", thing);
+ }
+ stack.push(functionage);
+ block_stack.push(blockage);
+ functionage = thing;
+ blockage = thing;
+ thing.live = [];
+ if (typeof thing.name === "object") {
+ thing.name.dead = false;
+ thing.name.init = true;
+ }
+ if (thing.extra === "get") {
+ if (thing.parameters.length !== 0) {
+ warn("bad_get", thing);
+ }
+ } else if (thing.extra === "set") {
+ if (thing.parameters.length !== 1) {
+ warn("bad_set", thing);
}
- name.dead = false;
- blockage.live.push(name);
}
+ thing.parameters.forEach(function (name) {
+ walk_expression(name.expression);
+ if (name.id === "{" || name.id === "[") {
+ name.names.forEach(subactivate);
+ } else {
+ name.dead = false;
+ name.init = true;
+ }
+ });
+}
- function action_var(thing) {
- thing.names.forEach(activate);
+function bitwise_check(thing) {
+ if (!option.bitwise && bitwiseop[thing.id] === true) {
+ warn("unexpected_a", thing);
}
+ if (
+ thing.id !== "("
+ && thing.id !== "&&"
+ && thing.id !== "||"
+ && thing.id !== "="
+ && Array.isArray(thing.expression)
+ && thing.expression.length === 2
+ && (
+ relationop[thing.expression[0].id] === true
+ || relationop[thing.expression[1].id] === true
+ )
+ ) {
+ warn("unexpected_a", thing);
+ }
+}
- preaction('assignment', bitwise_check);
- preaction('binary', bitwise_check);
- preaction('binary', function (thing) {
- if (relationop[thing.id] === true) {
- var left = thing.expression[0],
- right = thing.expression[1];
- if (left.id === 'NaN' || right.id === 'NaN') {
- warn('isNaN', thing);
- } else if (left.id === 'typeof') {
- if (right.id !== '(string)') {
- if (right.id !== 'typeof') {
- warn('expected_string_a', right);
- }
- } else {
- var value = right.value;
- if (value === 'symbol') {
- if (!option.es6) {
- warn('es6', right, value);
- }
- } else if (value === 'null' || value === 'undefined') {
- warn('unexpected_typeof_a', right, value);
- } else if (
- value !== 'boolean' &&
- value !== 'function' &&
- value !== 'number' &&
- value !== 'object' &&
- value !== 'string'
- ) {
- warn('expected_type_string_a', right, value);
- }
- }
- }
- }
+function pop_block() {
+ blockage.live.forEach(function (name) {
+ name.dead = true;
});
- preaction('binary', '==', function (thing) {
- warn('expected_a_b', thing, '===', '==');
- });
- preaction('binary', '!=', function (thing) {
- warn('expected_a_b', thing, '!==', '!=');
- });
- preaction('binary', '=>', preaction_function);
- preaction('binary', '||', function (thing) {
- thing.expression.forEach(function (thang) {
- if (thang.id === '&&' && !thang.wrapped) {
- warn('and', thang);
- }
- });
- });
- preaction('binary', '(', function (thing) {
- var left = thing.expression[0];
- if (
- left.identifier &&
- functionage.context[left.id] === undefined &&
- typeof functionage.name === 'object'
- ) {
- var parent = functionage.name.function;
- if (parent) {
- var left_variable = parent.context[left.id];
- if (
- left_variable !== undefined &&
- left_variable.dead &&
- left_variable.function === parent &&
- left_variable.calls !== undefined &&
- left_variable.calls[functionage.name.id] !== undefined
+ delete blockage.live;
+ blockage = block_stack.pop();
+}
+
+function activate(name) {
+ name.dead = false;
+ if (name.expression !== undefined) {
+ walk_expression(name.expression);
+ if (name.id === "{" || name.id === "[") {
+ name.names.forEach(subactivate);
+ } else {
+ name.init = true;
+ }
+ }
+ blockage.live.push(name);
+}
+
+function action_var(thing) {
+ thing.names.forEach(activate);
+}
+
+preaction("assignment", bitwise_check);
+preaction("binary", bitwise_check);
+preaction("binary", function (thing) {
+ if (relationop[thing.id] === true) {
+ const left = thing.expression[0];
+ const right = thing.expression[1];
+ if (left.id === "NaN" || right.id === "NaN") {
+ warn("number_isNaN", thing);
+ } else if (left.id === "typeof") {
+ if (right.id !== "(string)") {
+ if (right.id !== "typeof") {
+ warn("expected_string_a", right);
+ }
+ } else {
+ const value = right.value;
+ if (value === "null" || value === "undefined") {
+ warn("unexpected_typeof_a", right, value);
+ } else if (
+ value !== "boolean"
+ && value !== "function"
+ && value !== "number"
+ && value !== "object"
+ && value !== "string"
+ && value !== "symbol"
) {
- left_variable.dead = false;
+ warn("expected_type_string_a", right, value);
}
}
}
- });
- preaction('binary', 'in', function (thing) {
- warn('infix_in', thing);
- });
- preaction('statement', '{', function (thing) {
- block_stack.push(blockage);
- blockage = thing;
- thing.live = [];
- });
- preaction('statement', 'for', function (thing) {
- if (thing.name !== undefined) {
- var the_variable = lookup(thing.name);
- if (the_variable !== undefined) {
- the_variable.init = true;
- if (!the_variable.writable) {
- warn('bad_assignment_a', thing.name);
- }
- }
+ }
+});
+preaction("binary", "==", function (thing) {
+ warn("expected_a_b", thing, "===", "==");
+});
+preaction("binary", "!=", function (thing) {
+ warn("expected_a_b", thing, "!==", "!=");
+});
+preaction("binary", "=>", preaction_function);
+preaction("binary", "||", function (thing) {
+ thing.expression.forEach(function (thang) {
+ if (thang.id === "&&" && !thang.wrapped) {
+ warn("and", thang);
}
- walk_statement(thing.initial);
});
- preaction('statement', 'function', preaction_function);
- preaction('unary', '~', bitwise_check);
- preaction('unary', 'function', preaction_function);
- preaction('variable', function (thing) {
- var the_variable = lookup(thing);
- if (the_variable !== undefined) {
- thing.variable = the_variable;
- the_variable.used += 1;
+});
+preaction("binary", "(", function (thing) {
+ const left = thing.expression[0];
+ if (
+ left.identifier
+ && functionage.context[left.id] === undefined
+ && typeof functionage.name === "object"
+ ) {
+ const parent = functionage.name.parent;
+ if (parent) {
+ const left_variable = parent.context[left.id];
+ if (
+ left_variable !== undefined
+ && left_variable.dead
+ && left_variable.parent === parent
+ && left_variable.calls !== undefined
+ && left_variable.calls[functionage.name.id] !== undefined
+ ) {
+ left_variable.dead = false;
+ }
}
- });
-
- function init_variable(name) {
- var the_variable = lookup(name);
+ }
+});
+preaction("binary", "in", function (thing) {
+ warn("infix_in", thing);
+});
+preaction("binary", "instanceof", function (thing) {
+ warn("unexpected_a", thing);
+});
+preaction("binary", ".", function (thing) {
+ if (thing.expression.new) {
+ thing.new = true;
+ }
+});
+preaction("statement", "{", function (thing) {
+ block_stack.push(blockage);
+ blockage = thing;
+ thing.live = [];
+});
+preaction("statement", "for", function (thing) {
+ if (thing.name !== undefined) {
+ const the_variable = lookup(thing.name);
if (the_variable !== undefined) {
- if (the_variable.writable) {
- the_variable.init = true;
- return;
+ the_variable.init = true;
+ if (!the_variable.writable) {
+ warn("bad_assignment_a", thing.name);
}
}
- warn('bad_assignment_a', name);
}
+ walk_statement(thing.initial);
+});
+preaction("statement", "function", preaction_function);
+preaction("unary", "~", bitwise_check);
+preaction("unary", "function", preaction_function);
+preaction("variable", function (thing) {
+ const the_variable = lookup(thing);
+ if (the_variable !== undefined) {
+ thing.variable = the_variable;
+ the_variable.used += 1;
+ }
+});
- postaction('assignment', function (thing) {
-
-// Assignment using = sets the init property of a variable. No other assignment
-// operator can do this. A = token keeps that variable (or array of variables
-// in case of destructuring) in its name property.
-
- if (thing.id === '=') {
- if (thing.names !== undefined) {
- if (Array.isArray(thing.names)) {
- thing.names.forEach(init_variable);
- } else {
- init_variable(thing.names);
- }
- }
- } else {
- var lvalue = thing.expression[0];
- if (lvalue.arity === 'variable') {
- if (!lvalue.variable || lvalue.variable.writable !== true) {
- warn('bad_assignment_a', lvalue);
- }
- }
+function init_variable(name) {
+ const the_variable = lookup(name);
+ if (the_variable !== undefined) {
+ if (the_variable.writable) {
+ the_variable.init = true;
+ return;
}
- });
+ }
+ warn("bad_assignment_a", name);
+}
- function postaction_function(thing) {
- delete functionage.loop;
- delete functionage.switch;
- functionage = stack.pop();
- if (thing.wrapped) {
- warn('unexpected_parens', thing);
+postaction("assignment", "+=", function (thing) {
+ let right = thing.expression[1];
+ if (right.constant) {
+ if (
+ right.value === ""
+ || (right.id === "(number)" && right.value === "0")
+ || right.id === "(boolean)"
+ || right.id === "null"
+ || right.id === "undefined"
+ || Number.isNaN(right.value)
+ ) {
+ warn("unexpected_a", right);
}
- return pop_block();
}
+});
+postaction("assignment", function (thing) {
- postaction('binary', function (thing) {
- var right;
- if (relationop[thing.id]) {
- if (
- is_weird(thing.expression[0]) ||
- is_weird(thing.expression[1]) ||
- are_similar(thing.expression[0], thing.expression[1]) ||
- (
- thing.expression[0].constant === true &&
- thing.expression[1].constant === true
- )
- ) {
- warn('weird_relation_a', thing);
+// Assignment using = sets the init property of a variable. No other assignment
+// operator can do this. A = token keeps that variable (or array of variables
+// in case of destructuring) in its name property.
+
+ const lvalue = thing.expression[0];
+ if (thing.id === "=") {
+ if (thing.names !== undefined) {
+ if (Array.isArray(thing.names)) {
+ thing.names.forEach(init_variable);
+ } else {
+ init_variable(thing.names);
}
- }
- switch (thing.id) {
- case '+':
- case '-':
- right = thing.expression[1];
- if (
- right.id === thing.id &&
- right.arity === 'unary' &&
- !right.wrapped
+ } else {
+ if (lvalue.id === "[" || lvalue.id === "{") {
+ lvalue.expression.forEach(function (thing) {
+ if (thing.variable) {
+ thing.variable.init = true;
+ }
+ });
+ } else if (
+ lvalue.id === "."
+ && thing.expression[1].id === "undefined"
) {
- warn('wrap_unary', right);
- }
- break;
- case '=>':
- case '(':
- break;
- case '.':
- if (thing.expression.id === 'RegExp') {
- warn('weird_expression_a', thing);
+ warn(
+ "expected_a_b",
+ lvalue.expression,
+ "delete",
+ "undefined"
+ );
}
- break;
- default:
- if (
- thing.expression[0].constant === true &&
- thing.expression[1].constant === true
- ) {
- thing.constant = true;
+ }
+ } else {
+ if (lvalue.arity === "variable") {
+ if (!lvalue.variable || lvalue.variable.writable !== true) {
+ warn("bad_assignment_a", lvalue);
}
}
- });
- postaction('binary', '&&', function (thing) {
+ const right = syntax[thing.expression[1].id];
if (
- is_weird(thing.expression[0]) ||
- are_similar(thing.expression[0], thing.expression[1]) ||
- thing.expression[0].constant === true ||
- thing.expression[1].constant === true
+ right !== undefined
+ && (
+ right.id === "function"
+ || right.id === "=>"
+ || (
+ right.constant
+ && right.id !== "(number)"
+ && (right.id !== "(string)" || thing.id !== "+=")
+ )
+ )
) {
- warn('weird_condition_a', thing);
+ warn("unexpected_a", thing.expression[1]);
}
- });
- postaction('binary', '||', function (thing) {
+ }
+});
+
+function postaction_function(thing) {
+ delete functionage.finally;
+ delete functionage.loop;
+ delete functionage.switch;
+ delete functionage.try;
+ functionage = stack.pop();
+ if (thing.wrapped) {
+ warn("unexpected_parens", thing);
+ }
+ return pop_block();
+}
+
+postaction("binary", function (thing) {
+ let right;
+ if (relationop[thing.id]) {
if (
- is_weird(thing.expression[0]) ||
- are_similar(thing.expression[0], thing.expression[1]) ||
- thing.expression[0].constant === true
+ is_weird(thing.expression[0])
+ || is_weird(thing.expression[1])
+ || are_similar(thing.expression[0], thing.expression[1])
+ || (
+ thing.expression[0].constant === true
+ && thing.expression[1].constant === true
+ )
) {
- warn('weird_condition_a', thing);
+ warn("weird_relation_a", thing);
}
- });
- postaction('binary', '=>', postaction_function);
- postaction('binary', '(', function (thing) {
- if (!thing.wrapped && thing.expression[0].id === 'function') {
- warn('wrap_immediate', thing);
+ }
+ if (thing.id === "+") {
+ if (!option.convert) {
+ if (thing.expression[0].value === "") {
+ warn("expected_a_b", thing, "String(...)", "\"\" +");
+ } else if (thing.expression[1].value === "") {
+ warn("expected_a_b", thing, "String(...)", "+ \"\"");
+ }
}
- });
- postaction('binary', '[', function (thing) {
- if (thing.expression[0].id === 'RegExp') {
- warn('weird_expression_a', thing);
+ } else if (thing.id === "[") {
+ if (thing.expression[0].id === "window") {
+ warn("weird_expression_a", thing, "window[...]");
}
- if (is_weird(thing.expression[1])) {
- warn('weird_expression_a', thing.expression[1]);
+ if (thing.expression[0].id === "self") {
+ warn("weird_expression_a", thing, "self[...]");
}
- });
- postaction('statement', '{', pop_block);
- postaction('statement', 'const', action_var);
- postaction('statement', 'export', top_level_only);
- postaction('statement', 'for', function (thing) {
- walk_statement(thing.inc);
- });
- postaction('statement', 'function', postaction_function);
- postaction('statement', 'import', function (the_thing) {
- var name = the_thing.name;
- name.init = true;
- name.dead = false;
- blockage.live.push(name);
- return top_level_only(the_thing);
- });
- postaction('statement', 'let', action_var);
- postaction('statement', 'try', function (thing) {
- if (thing.catch !== undefined) {
- var the_name = thing.catch.name;
- if (the_name !== undefined) {
- var the_variable = functionage.context[the_name.id];
- the_variable.dead = false;
- the_variable.init = true;
- }
- walk_statement(thing.catch.block);
+ } else if (thing.id === "." || thing.id === "?.") {
+ if (thing.expression.id === "RegExp") {
+ warn("weird_expression_a", thing);
}
- });
- postaction('statement', 'var', action_var);
- postaction('ternary', function (thing) {
+ } else if (thing.id !== "=>" && thing.id !== "(") {
+ right = thing.expression[1];
if (
- is_weird(thing.expression[0]) ||
- thing.expression[0].constant === true ||
- are_similar(thing.expression[1], thing.expression[2])
+ (thing.id === "+" || thing.id === "-")
+ && right.id === thing.id
+ && right.arity === "unary"
+ && !right.wrapped
) {
- warn('unexpected_a', thing);
- } else if (are_similar(thing.expression[0], thing.expression[1])) {
- warn('expected_a_b', thing, '||', '?');
- } else if (are_similar(thing.expression[0], thing.expression[2])) {
- warn('expected_a_b', thing, '&&', '?');
- } else if (
- thing.expression[1].id === 'true' &&
- thing.expression[2].id === 'false'
- ) {
- warn('expected_a_b', thing, '!!', '?');
- } else if (
- thing.expression[1].id === 'false' &&
- thing.expression[2].id === 'true'
+ warn("wrap_unary", right);
+ }
+ if (
+ thing.expression[0].constant === true
+ && right.constant === true
) {
- warn('expected_a_b', thing, '!', '?');
- } else if (thing.expression[0].wrapped !== true && (
- thing.expression[0].id === '||' ||
- thing.expression[0].id === '&&'
- )) {
- warn('wrap_condition', thing.expression[0]);
+ thing.constant = true;
}
- });
- postaction('unary', function (thing) {
- switch (thing.id) {
- case '[':
- case '{':
- case 'function':
- case 'new':
- break;
- case '`':
- if (thing.expression.every(function (thing) {
- return thing.constant;
- })) {
- thing.constant = true;
+ }
+});
+postaction("binary", "&&", function (thing) {
+ if (
+ is_weird(thing.expression[0])
+ || are_similar(thing.expression[0], thing.expression[1])
+ || thing.expression[0].constant === true
+ || thing.expression[1].constant === true
+ ) {
+ warn("weird_condition_a", thing);
+ }
+});
+postaction("binary", "||", function (thing) {
+ if (
+ is_weird(thing.expression[0])
+ || are_similar(thing.expression[0], thing.expression[1])
+ || thing.expression[0].constant === true
+ ) {
+ warn("weird_condition_a", thing);
+ }
+});
+postaction("binary", "=>", postaction_function);
+postaction("binary", "(", function (thing) {
+ let left = thing.expression[0];
+ let the_new;
+ let arg;
+ if (left.id === "new") {
+ the_new = left;
+ left = left.expression;
+ }
+ if (left.id === "function") {
+ if (!thing.wrapped) {
+ warn("wrap_immediate", thing);
+ }
+ } else if (left.identifier) {
+ if (the_new !== undefined) {
+ if (
+ left.id[0] > "Z"
+ || left.id === "Boolean"
+ || left.id === "Number"
+ || left.id === "String"
+ || left.id === "Symbol"
+ ) {
+ warn("unexpected_a", the_new);
+ } else if (left.id === "Function") {
+ if (!option.eval) {
+ warn("unexpected_a", left, "new Function");
+ }
+ } else if (left.id === "Array") {
+ arg = thing.expression;
+ if (arg.length !== 2 || arg[1].id === "(string)") {
+ warn("expected_a_b", left, "[]", "new Array");
+ }
+ } else if (left.id === "Object") {
+ warn(
+ "expected_a_b",
+ left,
+ "Object.create(null)",
+ "new Object"
+ );
}
- break;
- default:
- if (thing.expression.constant === true) {
- thing.constant = true;
+ } else {
+ if (
+ left.id[0] >= "A"
+ && left.id[0] <= "Z"
+ && left.id !== "Boolean"
+ && left.id !== "Number"
+ && left.id !== "String"
+ && left.id !== "Symbol"
+ ) {
+ warn(
+ "expected_a_before_b",
+ left,
+ "new",
+ artifact(left)
+ );
}
}
- });
- postaction('unary', 'function', postaction_function);
-
- function delve(the_function) {
- Object.keys(the_function.context).forEach(function (id) {
- if (id !== 'ignore') {
- var name = the_function.context[id];
- if (name.function === the_function) {
- if (name.used === 0) {
- warn('unused_a', name);
- } else if (!name.init) {
- warn('uninitialized_a', name);
+ } else if (left.id === ".") {
+ let cack = the_new !== undefined;
+ if (left.expression.id === "Date" && left.name.id === "UTC") {
+ cack = !cack;
+ }
+ if (rx_cap.test(left.name.id) !== cack) {
+ if (the_new !== undefined) {
+ warn("unexpected_a", the_new);
+ } else {
+ warn(
+ "expected_a_before_b",
+ left.expression,
+ "new",
+ left.name.id
+ );
+ }
+ }
+ if (left.name.id === "getTime") {
+ const paren = left.expression;
+ if (paren.id === "(") {
+ const array = paren.expression;
+ if (array.length === 1) {
+ const new_date = array[0];
+ if (
+ new_date.id === "new"
+ && new_date.expression.id === "Date"
+ ) {
+ warn(
+ "expected_a_b",
+ new_date,
+ "Date.now()",
+ "new Date().getTime()"
+ );
}
}
}
+ }
+ }
+});
+postaction("binary", "[", function (thing) {
+ if (thing.expression[0].id === "RegExp") {
+ warn("weird_expression_a", thing);
+ }
+ if (is_weird(thing.expression[1])) {
+ warn("weird_expression_a", thing.expression[1]);
+ }
+});
+postaction("statement", "{", pop_block);
+postaction("statement", "const", action_var);
+postaction("statement", "export", top_level_only);
+postaction("statement", "for", function (thing) {
+ walk_statement(thing.inc);
+});
+postaction("statement", "function", postaction_function);
+postaction("statement", "import", function (the_thing) {
+ const name = the_thing.name;
+ if (Array.isArray(name)) {
+ name.forEach(function (name) {
+ name.dead = false;
+ name.init = true;
+ blockage.live.push(name);
});
+ } else {
+ name.dead = false;
+ name.init = true;
+ blockage.live.push(name);
}
+ return top_level_only(the_thing);
+});
+postaction("statement", "let", action_var);
+postaction("statement", "try", function (thing) {
+ if (thing.catch !== undefined) {
+ const the_name = thing.catch.name;
+ if (the_name !== undefined) {
+ const the_variable = functionage.context[the_name.id];
+ the_variable.dead = false;
+ the_variable.init = true;
+ }
+ walk_statement(thing.catch.block);
+ }
+});
+postaction("statement", "var", action_var);
+postaction("ternary", function (thing) {
+ if (
+ is_weird(thing.expression[0])
+ || thing.expression[0].constant === true
+ || are_similar(thing.expression[1], thing.expression[2])
+ ) {
+ warn("unexpected_a", thing);
+ } else if (are_similar(thing.expression[0], thing.expression[1])) {
+ warn("expected_a_b", thing, "||", "?");
+ } else if (are_similar(thing.expression[0], thing.expression[2])) {
+ warn("expected_a_b", thing, "&&", "?");
+ } else if (
+ thing.expression[1].id === "true"
+ && thing.expression[2].id === "false"
+ ) {
+ warn("expected_a_b", thing, "!!", "?");
+ } else if (
+ thing.expression[1].id === "false"
+ && thing.expression[2].id === "true"
+ ) {
+ warn("expected_a_b", thing, "!", "?");
+ } else if (
+ thing.expression[0].wrapped !== true
+ && (
+ thing.expression[0].id === "||"
+ || thing.expression[0].id === "&&"
+ )
+ ) {
+ warn("wrap_condition", thing.expression[0]);
+ }
+});
+postaction("unary", function (thing) {
+ if (thing.id === "`") {
+ if (thing.expression.every(function (thing) {
+ return thing.constant;
+ })) {
+ thing.constant = true;
+ }
+ } else if (thing.id === "!") {
+ if (thing.expression.constant === true) {
+ warn("unexpected_a", thing);
+ }
+ } else if (thing.id === "!!") {
+ if (!option.convert) {
+ warn("expected_a_b", thing, "Boolean(...)", "!!");
+ }
+ } else if (
+ thing.id !== "["
+ && thing.id !== "{"
+ && thing.id !== "function"
+ && thing.id !== "new"
+ ) {
+ if (thing.expression.constant === true) {
+ thing.constant = true;
+ }
+ }
+});
+postaction("unary", "function", postaction_function);
+postaction("unary", "+", function (thing) {
+ if (!option.convert) {
+ warn("expected_a_b", thing, "Number(...)", "+");
+ }
+ const right = thing.expression;
+ if (right.id === "(" && right.expression[0].id === "new") {
+ warn("unexpected_a_before_b", thing, "+", "new");
+ } else if (
+ right.constant
+ || right.id === "{"
+ || (right.id === "[" && right.arity !== "binary")
+ ) {
+ warn("unexpected_a", thing, "+");
+ }
+});
+
+function delve(the_function) {
+ Object.keys(the_function.context).forEach(function (id) {
+ if (id !== "ignore") {
+ const name = the_function.context[id];
+ if (name.parent === the_function) {
+ if (
+ name.used === 0
+ && (
+ name.role !== "function"
+ || name.parent.arity !== "unary"
+ )
+ ) {
+ warn("unused_a", name);
+ } else if (!name.init) {
+ warn("uninitialized_a", name);
+ }
+ }
+ }
+ });
+}
- function uninitialized_and_unused() {
+function uninitialized_and_unused() {
// Delve into the functions looking for variables that were not initialized
// or used. If the file imports or exports, then its global object is also
// delved.
- if (module_mode === true || option.node) {
- delve(global);
- }
- functions.forEach(delve);
+ if (module_mode === true || option.node) {
+ delve(global);
}
+ functions.forEach(delve);
+}
// Go through the token list, looking at usage of whitespace.
- function whitage() {
- var closer = '(end)',
- free = false,
- left = global,
- margin = 0,
- nr_comments_skipped = 0,
- open = true,
- qmark = '',
- result,
- right;
-
- function expected_at(at) {
+function whitage() {
+ let closer = "(end)";
+ let free = false;
+ let left = global;
+ let margin = 0;
+ let nr_comments_skipped = 0;
+ let open = true;
+ let opening = true;
+ let right;
+
+ function pop() {
+ const previous = stack.pop();
+ closer = previous.closer;
+ free = previous.free;
+ margin = previous.margin;
+ open = previous.open;
+ opening = previous.opening;
+ }
+
+ function push() {
+ stack.push({
+ closer,
+ free,
+ margin,
+ open,
+ opening
+ });
+ }
+
+ function expected_at(at) {
+ warn(
+ "expected_a_at_b_c",
+ right,
+ artifact(right),
+ fudge + at,
+ artifact_column(right)
+ );
+ }
+
+ function at_margin(fit) {
+ const at = margin + fit;
+ if (right.from !== at) {
+ return expected_at(at);
+ }
+ }
+
+ function no_space_only() {
+ if (
+ left.id !== "(global)"
+ && left.nr + 1 === right.nr
+ && (
+ left.line !== right.line
+ || left.thru !== right.from
+ )
+ ) {
warn(
- 'expected_a_at_b_c',
+ "unexpected_space_a_b",
right,
- artifact(right),
- fudge + at,
- artifact_column(right)
+ artifact(left),
+ artifact(right)
);
}
+ }
- function at_margin(fit) {
- var at = margin + fit;
- if (right.from !== at) {
- return expected_at(at);
- }
- }
-
- function no_space_only() {
- if (left.line !== right.line || left.thru !== right.from) {
+ function no_space() {
+ if (left.line === right.line) {
+ if (left.thru !== right.from && nr_comments_skipped === 0) {
warn(
- 'unexpected_space_a_b',
+ "unexpected_space_a_b",
right,
artifact(left),
artifact(right)
);
}
- }
-
- function no_space() {
- if (left.line === right.line) {
- if (left.thru !== right.from && nr_comments_skipped === 0) {
- warn(
- 'unexpected_space_a_b',
- right,
- artifact(left),
- artifact(right)
- );
+ } else {
+ if (open) {
+ const at = (
+ free
+ ? margin
+ : margin + 8
+ );
+ if (right.from < at) {
+ expected_at(at);
}
} else {
- if (open) {
- var at = (free)
- ? margin
- : margin + 8;
- if (right.from < at) {
- expected_at(at);
- }
- } else {
- if (right.from !== margin + 8) {
- expected_at(margin + 8);
- }
+ if (right.from !== margin + 8) {
+ expected_at(margin + 8);
}
}
}
+ }
+
+ function one_space_only() {
+ if (left.line !== right.line || left.thru + 1 !== right.from) {
+ warn(
+ "expected_space_a_b",
+ right,
+ artifact(left),
+ artifact(right)
+ );
+ }
+ }
- function one_space_only() {
- if (left.line !== right.line || left.thru + 1 !== right.from) {
+ function one_space() {
+ if (left.line === right.line || !open) {
+ if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
warn(
- 'expected_space_a_b',
+ "expected_space_a_b",
right,
artifact(left),
artifact(right)
);
}
- }
-
- function one_space() {
- if (left.line === right.line) {
- if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
- warn(
- 'expected_space_a_b',
- right,
- artifact(left),
- artifact(right)
- );
- }
- } else {
- if (free) {
- if (right.from < margin) {
- expected_at(margin);
- }
- } else {
- if (right.from !== margin + 8) {
- expected_at(margin + 8);
- }
- }
- }
- }
-
- function unqmark() {
-
-// Undo the effects of dangling nested ternary operators.
-
- var level = qmark.length;
- if (level > 0) {
- margin -= level * 4;
+ } else {
+ if (right.from !== margin) {
+ expected_at(margin);
}
- qmark = '';
}
+ }
- stack = [];
- tokens.forEach(function (the_token) {
- right = the_token;
- if (right.id === '(comment)' || right.id === '(end)') {
- nr_comments_skipped += 1;
- } else {
+ stack = [];
+ tokens.forEach(function (the_token) {
+ right = the_token;
+ if (right.id === "(comment)" || right.id === "(end)") {
+ nr_comments_skipped += 1;
+ } else {
// If left is an opener and right is not the closer, then push the previous
// state. If the token following the opener is on the next line, then this is
-// an open form. If the tokens are on different lines, then it is a closed for.
+// an open form. If the tokens are on the same line, then it is a closed form.
// Open form is more readable, with each item (statement, argument, parameter,
// etc) starting on its own line. Closed form is more compact. Statement blocks
// are always in open form.
- var new_closer = opener[left.id];
- if (typeof new_closer === 'string') {
- if (new_closer !== right.id) {
- stack.push({
- closer: closer,
- free: free,
- margin: margin,
- open: open,
- qmark: qmark
- });
- qmark = '';
- closer = new_closer;
- if (left.line !== right.line) {
- free = closer === ')' && left.free;
- open = true;
- margin += 4;
- if (right.role === 'label') {
- if (right.from !== 0) {
- expected_at(0);
- }
- } else if (right.switch) {
- unqmark();
- at_margin(-4);
- } else {
- at_margin(0);
+ const new_closer = opener[left.id];
+ if (typeof new_closer === "string") {
+ if (new_closer !== right.id) {
+ opening = left.open || (left.line !== right.line);
+ push();
+ closer = new_closer;
+ if (opening) {
+ free = closer === ")" && left.free;
+ open = true;
+ margin += 4;
+ if (right.role === "label") {
+ if (right.from !== 0) {
+ expected_at(0);
}
+ } else if (right.switch) {
+ at_margin(-4);
} else {
- if (right.statement || right.role === 'label') {
- warn(
- 'expected_line_break_a_b',
- right,
- artifact(left),
- artifact(right)
- );
- }
- free = false;
- open = false;
- no_space_only();
+ at_margin(0);
}
} else {
+ if (right.statement || right.role === "label") {
+ warn(
+ "expected_line_break_a_b",
+ right,
+ artifact(left),
+ artifact(right)
+ );
+ }
+ free = false;
+ open = false;
+ no_space_only();
+ }
+ } else {
// If left and right are opener and closer, then the placement of right depends
-// on the openness. Illegal pairs (like {]) have already been detected.
+// on the openness. Illegal pairs (like '{]') have already been detected.
- if (left.line === right.line) {
- no_space();
- } else {
- at_margin(0);
- }
+ if (left.line === right.line) {
+ no_space();
+ } else {
+ at_margin(0);
+ }
+ }
+ } else {
+ if (right.statement === true) {
+ if (left.id === "else") {
+ one_space_only();
+ } else {
+ at_margin(0);
+ open = false;
}
- } else {
-// If right is a closer, then pop the previous state,
+// If right is a closer, then pop the previous state.
- if (right.id === closer) {
- var previous = stack.pop();
- margin = previous.margin;
- if (open && right.id !== ';') {
- at_margin(0);
- } else {
- no_space_only();
- }
- closer = previous.closer;
- free = previous.free;
- open = previous.open;
- qmark = previous.qmark;
+ } else if (right.id === closer) {
+ pop();
+ if (opening && right.id !== ";") {
+ at_margin(0);
} else {
+ no_space_only();
+ }
+ } else {
-// Left is not an opener, and right is not a closer. The nature of left and
-// right will determine the space between them.
+// Left is not an opener, and right is not a closer.
+// The nature of left and right will determine the space between them.
-// If left is , or ; or right is a statement then if open, right must go at the
-// margin, or if closed, a space before.
+// If left is ',' or ';' or right is a statement then if open,
+// right must go at the margin, or if closed, a space between.
+ if (right.switch) {
+ at_margin(-4);
+ } else if (right.role === "label") {
+ if (right.from !== 0) {
+ expected_at(0);
+ }
+ } else if (left.id === ",") {
+ if (!open || (
+ (free || closer === "]")
+ && left.line === right.line
+ )) {
+ one_space();
+ } else {
+ at_margin(0);
+ }
- if (right.switch) {
- unqmark();
- at_margin(-4);
- } else if (right.role === 'label') {
- if (right.from !== 0) {
- expected_at(0);
- }
- } else if (left.id === ',') {
- unqmark();
- if (!open || (
- (free || closer === ']') &&
- left.line === right.line
- )) {
- one_space();
- } else {
- at_margin(0);
- }
+// If right is a ternary operator, line it up on the margin.
-// If right is a ternary operator, line it up on the margin. Use qmark to
-// deal with nested ternary operators.
-
- } else if (right.arity === 'ternary') {
- if (right.id === '?') {
- margin += 4;
- qmark += '?';
- } else {
- result = qmark.match(rx_colons);
- qmark = result[1] + ':';
- margin -= 4 * result[2].length;
- }
+ } else if (right.arity === "ternary") {
+ if (open) {
at_margin(0);
- } else if (right.arity === 'binary' && right.id === '(' && free) {
- no_space();
- } else if (
- left.id === '.' ||
- left.id === '...' ||
- right.id === ',' ||
- right.id === ';' ||
- right.id === ':' ||
- (right.arity === 'binary' && (
- right.id === '(' ||
- right.id === '['
- )) ||
- (right.arity === 'function' && left.id !== 'function')
- ) {
- no_space_only();
- } else if (right.id === '.') {
- if (left.line === right.line) {
- no_space();
- } else {
- if (!rx_dot.test(qmark)) {
- qmark += '.';
- margin += 4;
- }
- at_margin(0);
- }
- } else if (left.id === ';') {
- unqmark();
- if (open) {
- at_margin(0);
- } else {
- one_space();
- }
- } else if (
- left.arity === 'ternary' ||
- left.id === 'case' ||
- left.id === 'catch' ||
- left.id === 'else' ||
- left.id === 'finally' ||
- left.id === 'while' ||
- right.id === 'catch' ||
- right.id === 'else' ||
- right.id === 'finally' ||
- (right.id === 'while' && !right.statement) ||
- (left.id === ')' && right.id === '{')
- ) {
+ } else {
+ warn("use_open", right);
+ }
+ } else if (
+ right.arity === "binary"
+ && right.id === "("
+ && free
+ ) {
+ no_space();
+ } else if (
+ left.id === "."
+ || left.id === "?."
+ || left.id === "..."
+ || right.id === ","
+ || right.id === ";"
+ || right.id === ":"
+ || (
+ right.arity === "binary"
+ && (right.id === "(" || right.id === "[")
+ )
+ || (
+ right.arity === "function"
+ && left.id !== "function"
+ )
+ ) {
+ no_space_only();
+ } else if (right.id === "." || right.id === "?.") {
+ no_space_only();
+ } else if (left.id === ";") {
+ if (open) {
+ at_margin(0);
+ } else {
+ one_space();
+ }
+ } else if (
+ left.arity === "ternary"
+ || left.id === "case"
+ || left.id === "catch"
+ || left.id === "else"
+ || left.id === "finally"
+ || left.id === "while"
+ || right.id === "catch"
+ || right.id === "else"
+ || right.id === "finally"
+ || (right.id === "while" && !right.statement)
+ || (left.id === ")" && right.id === "{")
+ ) {
+ one_space_only();
+ } else if (
+ left.id === "var"
+ || left.id === "const"
+ || left.id === "let"
+ ) {
+ push();
+ closer = ";";
+ free = false;
+ open = left.open;
+ if (open) {
+ margin = margin + 4;
+ at_margin(0);
+ } else {
one_space_only();
- } else if (right.statement === true) {
- if (open) {
- at_margin(0);
- } else {
- one_space();
- }
- } else if (
- left.id === 'var' ||
- left.id === 'const' ||
- left.id === 'let'
- ) {
- stack.push({
- closer: closer,
- free: free,
- margin: margin,
- open: open,
- qmark: qmark
- });
- closer = ';';
- free = false;
- open = left.open;
- qmark = '';
- if (open) {
- margin = margin + 4;
- at_margin(0);
- } else {
- one_space_only();
- }
- } else if (
+ }
+ } else if (
// There is a space between left and right.
- spaceop[left.id] === true ||
- spaceop[right.id] === true ||
- (
- left.arity === 'binary' &&
- (left.id === '+' || left.id === '-')
- ) ||
- (
- right.arity === 'binary' &&
- (right.id === '+' || right.id === '-')
- ) ||
- left.id === 'function' ||
- left.id === ':' ||
+ spaceop[left.id] === true
+ || spaceop[right.id] === true
+ || (
+ left.arity === "binary"
+ && (left.id === "+" || left.id === "-")
+ )
+ || (
+ right.arity === "binary"
+ && (right.id === "+" || right.id === "-")
+ )
+ || left.id === "function"
+ || left.id === ":"
+ || (
(
- (
- left.identifier ||
- left.id === '(string)' ||
- left.id === '(number)'
- ) &&
- (
- right.identifier ||
- right.id === '(string)' ||
- right.id === '(number)'
- )
- ) ||
- (left.arity === 'statement' && right.id !== ';')
- ) {
- one_space();
- } else if (left.arity === 'unary') {
- no_space_only();
- }
+ left.identifier
+ || left.id === "(string)"
+ || left.id === "(number)"
+ )
+ && (
+ right.identifier
+ || right.id === "(string)"
+ || right.id === "(number)"
+ )
+ )
+ || (left.arity === "statement" && right.id !== ";")
+ ) {
+ one_space();
+ } else if (left.arity === "unary" && left.id !== "`") {
+ no_space_only();
}
}
- nr_comments_skipped = 0;
- delete left.calls;
- delete left.dead;
- delete left.free;
- delete left.init;
- delete left.open;
- delete left.used;
- left = right;
}
- });
- }
+ nr_comments_skipped = 0;
+ delete left.calls;
+ delete left.dead;
+ delete left.free;
+ delete left.init;
+ delete left.open;
+ delete left.used;
+ left = right;
+ }
+ });
+}
// The jslint function itself.
- return function (source, option_object, global_array) {
- try {
- warnings = [];
- option = option_object || empty();
- anon = "anonymous";
- block_stack = [];
- declared_globals = empty();
- directive_mode = true;
- early_stop = true;
- export_mode = false;
- fudge = (option.fudge)
- ? 1
- : 0;
- functions = [];
- global = {
- id: '(global)',
- body: true,
- context: empty(),
- from: 0,
- level: 0,
- line: 0,
- live: [],
- loop: 0,
- switch: 0,
- thru: 0
- };
- blockage = global;
- functionage = global;
- imports = [];
- json_mode = false;
- mega_mode = false;
- module_mode = false;
- next_token = global;
- property = empty();
- stack = [];
- tenure = undefined;
- token = global;
- token_nr = 0;
- var_mode = undefined;
- populate(declared_globals, standard, false);
- if (global_array !== undefined) {
- populate(declared_globals, global_array, false);
- }
- Object.keys(option).forEach(function (name) {
- if (option[name] === true) {
- var allowed = allowed_option[name];
- if (Array.isArray(allowed)) {
- populate(declared_globals, allowed, false);
- }
+export default Object.freeze(function jslint(
+ source = "",
+ option_object = empty(),
+ global_array = []
+) {
+ try {
+ warnings = [];
+ option = Object.assign(empty(), option_object);
+ anon = "anonymous";
+ block_stack = [];
+ declared_globals = empty();
+ directive_mode = true;
+ directives = [];
+ early_stop = true;
+ exports = empty();
+ froms = [];
+ fudge = (
+ option.fudge
+ ? 1
+ : 0
+ );
+ functions = [];
+ global = {
+ id: "(global)",
+ body: true,
+ context: empty(),
+ from: 0,
+ level: 0,
+ line: 0,
+ live: [],
+ loop: 0,
+ switch: 0,
+ thru: 0
+ };
+ blockage = global;
+ functionage = global;
+ json_mode = false;
+ mega_mode = false;
+ module_mode = false;
+ next_token = global;
+ property = empty();
+ shebang = false;
+ stack = [];
+ tenure = undefined;
+ token = global;
+ token_nr = 0;
+ var_mode = undefined;
+ populate(standard, declared_globals, false);
+ populate(global_array, declared_globals, false);
+ Object.keys(option).forEach(function (name) {
+ if (option[name] === true) {
+ const allowed = allowed_option[name];
+ if (Array.isArray(allowed)) {
+ populate(allowed, declared_globals, false);
}
- });
- tokenize(source);
- advance();
- if (tokens[0].id === '{' || tokens[0].id === '[') {
- json_mode = true;
- tree = json_value();
- advance('(end)');
- } else {
+ }
+ });
+ tokenize(source);
+ advance();
+ if (json_mode) {
+ tree = json_value();
+ advance("(end)");
+ } else {
// Because browsers encourage combining of script files, the first token might
// be a semicolon to defend against a missing semicolon in the preceding file.
- if (option.browser) {
- if (next_token.id === ';') {
- advance(';');
- }
- } else {
+ if (option.browser) {
+ if (next_token.id === ";") {
+ advance(";");
+ }
+ } else {
// If we are not in a browser, then the file form of strict pragma may be used.
- if (
- next_token.id === '(string)' &&
- next_token.value === 'use strict'
- ) {
- advance('(string)');
- advance(';');
- global.strict = true;
- }
+ if (
+ next_token.value === "use strict"
+ ) {
+ advance("(string)");
+ advance(";");
}
- tree = statements();
- advance('(end)');
- functionage = global;
- walk_statement(tree);
+ }
+ tree = statements();
+ advance("(end)");
+ functionage = global;
+ walk_statement(tree);
+ if (warnings.length === 0) {
uninitialized_and_unused();
if (!option.white) {
whitage();
}
}
- early_stop = false;
- } catch (e) {
- if (e.name !== 'JSLintError') {
- warnings.push(e);
- }
}
- return {
- edition: "2015-10-29",
- functions: functions,
- global: global,
- id: "(JSLint)",
- imports: imports,
- json: json_mode,
- lines: lines,
- module: module_mode === true,
- ok: warnings.length === 0 && !early_stop,
- option: option,
- property: property,
- stop: early_stop,
- tokens: tokens,
- tree: tree,
- warnings: warnings.sort(function (a, b) {
- return a.line - b.line || a.column - b.column;
- })
- };
+ if (!option.browser) {
+ directives.forEach(function (comment) {
+ if (comment.directive === "global") {
+ warn("missing_browser", comment);
+ }
+ });
+ }
+ early_stop = false;
+ } catch (e) {
+ if (e.name !== "JSLintError") {
+ warnings.push(e);
+ }
+ }
+ return {
+ directives,
+ edition: "2019-01-31",
+ exports,
+ froms,
+ functions,
+ global,
+ id: "(JSLint)",
+ json: json_mode,
+ lines,
+ module: module_mode === true,
+ ok: warnings.length === 0 && !early_stop,
+ option,
+ property,
+ shebang: (
+ shebang
+ ? lines[0]
+ : undefined
+ ),
+ stop: early_stop,
+ tokens,
+ tree,
+ warnings: warnings.sort(function (a, b) {
+ return a.line - b.line || a.column - b.column;
+ })
};
-}());
+});
diff --git a/lib/main.js b/lib/main.js
index 456e0e4..7cf1732 100644
--- a/lib/main.js
+++ b/lib/main.js
@@ -1,55 +1,55 @@
-var nodelint = require('./nodelint');
-var optModule = require('./options');
+var nodelint = require("./nodelint");
+var optModule = require("./options");
var nopt = require("nopt");
-var exit = require('exit'),
- glob = require('glob');
+var exit = require("exit");
+var glob = require("glob");
-var LintStream = require('./lintstream.js'),
- ReportStream = require('./reportstream.js'),
- CollectorStream = require('./collectorstream.js'),
- JSONReportStream = require('./jsonreportstream.js'),
- FileOpener = require('./fileopener.js');
+var LintStream = require("./lintstream.js");
+var ReportStream = require("./reportstream.js");
+var CollectorStream = require("./collectorstream.js");
+var JSONReportStream = require("./jsonreportstream.js");
+var FileOpener = require("./fileopener.js");
var con = console;
var pro = process;
exports.setConsole = function (c) {
- 'use strict';
+ "use strict";
con = c;
};
exports.setProcess = function (p) {
- 'use strict';
+ "use strict";
pro = p;
exit = pro.exit.bind(pro);
};
function commandOptions() {
- 'use strict';
+ "use strict";
var commandOpts = {
- 'indent': Number,
- 'maxerr': Number,
- 'maxlen': Number,
- 'predef': [String, Array],
- 'edition': String,
- 'config': String
+ "indent": Number,
+ "maxerr": Number,
+ "maxlen": Number,
+ "predef": [String, Array],
+ "edition": String,
+ "config": String
},
/* flags defined in jslint-latest.js */
jslintFlags = [
- 'ass', 'bitwise', 'browser', 'closure', 'continue',
- 'debug', 'devel', 'eqeq', 'evil', 'forin', 'newcap',
- 'node', 'nomen', 'passfail', 'plusplus', 'properties',
- 'regexp', 'rhino', 'unparam', 'sloppy', 'stupid', 'sub',
- 'todo', 'vars', 'white'
+ "ass", "bitwise", "browser", "closure", "continue",
+ "debug", "devel", "eqeq", "evil", "forin", "newcap",
+ "node", "nomen", "passfail", "plusplus", "properties",
+ "regexp", "rhino", "unparam", "sloppy", "stupid", "sub",
+ "todo", "vars", "white"
],
/* flags used by node-jslint to control output */
cliFlags = [
- 'json', 'color', 'terse', 'version', 'filter'
+ "json", "color", "terse", "version", "filter"
],
/* not used by jslint-latest.js */
deprecatedFlags = [
- 'anon', 'es5', 'on', 'undef', 'windows'
+ "anon", "es5", "on", "undef", "windows"
],
allFlags = jslintFlags.concat(cliFlags).concat(deprecatedFlags);
@@ -62,7 +62,7 @@ function commandOptions() {
exports.commandOptions = commandOptions;
function die(why) {
- 'use strict';
+ "use strict";
var o = commandOptions();
con.warn(why);
con.warn("Usage: " + pro.argv[1] +
@@ -72,7 +72,7 @@ function die(why) {
}
function parseArgs(argv) {
- 'use strict';
+ "use strict";
var args = nopt(commandOptions(), {}, argv);
if (args.filter === undefined) {
@@ -84,19 +84,20 @@ function parseArgs(argv) {
exports.parseArgs = parseArgs;
exports.reportVersion = function reportVersion(callback, options) {
- 'use strict';
+ "use strict";
process.nextTick(function () {
- var package_data = require('../package.json'),
- version = package_data.version,
- edition = nodelint.load(options.edition).edition;
+ var package_data = require("../package.json");
+ var version = package_data.version;
+ var edition = nodelint.load(options.edition).edition;
- callback("node-jslint version: " + version + " JSLint edition " + edition);
+ callback("node-jslint version: " + version + " JSLint edition "
+ + edition);
});
};
function expandGlob(glob) {
- 'use strict';
+ "use strict";
return function (pattern) {
return glob.sync(pattern);
@@ -105,19 +106,19 @@ function expandGlob(glob) {
exports.expandGlob = expandGlob;
function noNodeModules(file) {
- 'use strict';
- return file.indexOf('node_modules') === -1;
+ "use strict";
+ return file.indexOf("node_modules") === -1;
}
exports.noNodeModules = noNodeModules;
function flatten(a, b) {
- 'use strict';
+ "use strict";
return a.concat(b);
}
function globFiles(list, glob, filter) {
- 'use strict';
+ "use strict";
var remain = [];
remain = list.map(expandGlob(glob))
@@ -132,7 +133,7 @@ function globFiles(list, glob, filter) {
exports.globFiles = globFiles;
function makeReporter(parsed) {
- 'use strict';
+ "use strict";
var reporter;
if (parsed.json) {
@@ -143,8 +144,8 @@ function makeReporter(parsed) {
reporter = new ReportStream(parsed);
}
- reporter.on('data', function (chunk) {
- if (chunk === '.') {
+ reporter.on("data", function (chunk) {
+ if (chunk === ".") {
pro.stderr.write(chunk);
} else {
con.log(chunk);
@@ -156,7 +157,7 @@ function makeReporter(parsed) {
exports.makeReporter = makeReporter;
exports.runMain = function (options, cb) {
- 'use strict';
+ "use strict";
if (options.version) {
exports.reportVersion(con.log, options);
@@ -176,7 +177,7 @@ exports.runMain = function (options, cb) {
opener.pipe(linter);
linter.pipe(reporter);
- reporter.on('finish', function () {
+ reporter.on("finish", function () {
if (cb) {
return cb(null, reporter.lint);
}
diff --git a/lib/options.js b/lib/options.js
index baf5597..8f14b7f 100644
--- a/lib/options.js
+++ b/lib/options.js
@@ -1,9 +1,9 @@
(function () {
- 'use strict';
+ "use strict";
- var path = require('path'),
- fs = require('fs'),
- con = console;
+ var path = require("path");
+ var fs = require("fs");
+ var con = console;
exports.setConsole = function (c) {
con = c;
@@ -56,7 +56,7 @@
return options;
}
- options.predef = options.predef.split(',').filter(notFalsy);
+ options.predef = options.predef.split(",").filter(notFalsy);
return options;
}
@@ -74,9 +74,9 @@
}
function mergeConfigs(home, project) {
- var homeConfig,
- cwdConfig,
- config;
+ var homeConfig;
+ var cwdConfig;
+ var config;
home.some(function (file) {
homeConfig = loadAndParseConfig(file);
@@ -95,12 +95,13 @@
exports.mergeConfigs = mergeConfigs;
function loadConfig(h, configFile) {
- var home = h || '',
- homeConfigs = ['.jslint.conf', '.jslintrc'],
- projectConfigs = ['jslint.conf', '.jslint.conf', 'jslintrc', '.jslintrc'];
+ var home = h || "";
+ var homeConfigs = [".jslint.conf", ".jslintrc"];
+ var projectConfigs = ["jslint.conf", ".jslint.conf", "jslintrc", ".jslintrc"];
if (configFile) {
- // explicitly specified config file overrides default config file name, path
+ // explicitly specified config file overrides
+ // default config file name, path
homeConfigs = [configFile];
} else {
homeConfigs = homeConfigs.map(function (file) {
diff --git a/lib/reporter.js b/lib/reporter.js
index 844f28c..b3d5f8f 100644
--- a/lib/reporter.js
+++ b/lib/reporter.js
@@ -1,5 +1,5 @@
(function () {
- 'use strict';
+ "use strict";
var color = require("./color");
@@ -24,12 +24,12 @@
};
exports.report = function (file, lint, colorize, terse) {
- var line,
- pad,
- errors,
- fudge = Number(lint.option && lint.option.fudge) || 0,
- logger = this.logger,
- fileMessage;
+ var line;
+ var pad;
+ var errors;
+ var fudge = Number(lint.option && lint.option.fudge) || 0;
+ var logger = this.logger;
+ var fileMessage;
function c(format, str) {
if (colorize) {
@@ -38,7 +38,7 @@
return str;
}
- fileMessage = "\n" + c('bold', file);
+ fileMessage = "\n" + c("bold", file);
function row(e) {
return e.line + fudge;
@@ -47,7 +47,7 @@
return (e.character || e.column) + fudge;
}
function evidence(e) {
- return e.evidence || (lint.lines && lint.lines[e.line]) || '';
+ return e.evidence || (lint.lines && lint.lines[e.line]) || "";
}
function message(e) {
return e.reason || e.message;
@@ -62,26 +62,26 @@
if (terse) {
errors.forEach(function (e) {
- logger.log(file + ':' + row(e) + ':' + col(e) + ': ' + message(e));
+ logger.log(file + ":" + row(e) + ":" + col(e) + ": " + message(e));
});
} else {
logger.log(fileMessage);
errors.forEach(function (e, i) {
pad = "#" + String(i + 1);
while (pad.length < 3) {
- pad = ' ' + pad;
+ pad = " " + pad;
}
- line = ' // Line ' + row(e) + ', Pos ' + col(e);
+ line = " // Line " + row(e) + ", Pos " + col(e);
- logger.log(pad + ' ' + c('yellow', message(e)));
- logger.log(' ' + evidence(e).trim() + c('grey', line));
+ logger.log(pad + " " + c("yellow", message(e)));
+ logger.log(" " + evidence(e).trim() + c("grey", line));
});
}
} else {
if (terse) {
logger.err(".");
} else {
- logger.log(fileMessage + " is " + c('green', 'OK') + ".");
+ logger.log(fileMessage + " is " + c("green", "OK") + ".");
}
}
diff --git a/test/color.js b/test/color.js
index 5093008..e69da8e 100644
--- a/test/color.js
+++ b/test/color.js
@@ -1,8 +1,8 @@
-var assert = require('assert'),
- color = require('../lib/color');
+var assert = require("assert");
+var color = require("../lib/color");
-suite('text colors', function () {
- test('bold text', function () {
- assert.equal('\x1b[1mbold\x1b[0m', color.bold('bold'));
+suite("text colors", function () {
+ test("bold text", function () {
+ assert.equal("\x1b[1mbold\x1b[0m", color.bold("bold"));
});
});
diff --git a/test/fileopener.js b/test/fileopener.js
index ee6a2f9..e2a63e9 100644
--- a/test/fileopener.js
+++ b/test/fileopener.js
@@ -1,31 +1,31 @@
-var assert = require('assert'),
- stream = require('stream'),
- FileOpener = require('../lib/fileopener.js');
+var assert = require("assert");
+var stream = require("stream");
+var FileOpener = require("../lib/fileopener.js");
-suite('fileopener', function () {
- test('can create object', function () {
+suite("fileopener", function () {
+ test("can create object", function () {
var f = new FileOpener();
});
- test('can open file', function (done) {
+ test("can open file", function (done) {
var f = new FileOpener();
- f.on('data', function(chunk) {
+ f.on("data", function(chunk) {
done();
});
- f.write('./README.md');
+ f.write("./README.md");
f.end();
});
- test('error on nonexistent file', function (done) {
+ test("error on nonexistent file", function (done) {
var f = new FileOpener();
- f.on('error', function(chunk) {
+ f.on("error", function(chunk) {
done();
});
- f.write('./NONEXISTENT-FILE.not-here');
+ f.write("./NONEXISTENT-FILE.not-here");
f.end();
});
});
diff --git a/test/fixtures/good.js b/test/fixtures/good.js
index 72d6d99..7aabe5a 100644
--- a/test/fixtures/good.js
+++ b/test/fixtures/good.js
@@ -1,4 +1,4 @@
-'use strict';
+"use strict";
function a(b) {
return b + 1;
diff --git a/test/linter.js b/test/linter.js
index b27cd94..2000f94 100644
--- a/test/linter.js
+++ b/test/linter.js
@@ -1,71 +1,71 @@
-var assert = require('assert'),
- nodelint = require('../lib/nodelint'),
- linter = require('../lib/linter');
+var assert = require("assert");
+var nodelint = require("../lib/nodelint");
+var linter = require("../lib/linter");
-suite('merge', function () {
- test('can merge when no conflict', function () {
+suite("merge", function () {
+ test("can merge when no conflict", function () {
assert.deepEqual({a: 1, b: 2}, linter.merge({a: 1}, {b: 2}));
});
- test('left side wins on merge', function () {
+ test("left side wins on merge", function () {
assert.deepEqual({a: 1}, linter.merge({a: 1}, {a: 2}));
});
- test('merge where one or more args is undefined', function () {
+ test("merge where one or more args is undefined", function () {
assert.deepEqual({a: 1}, linter.merge({a: 1}, undefined));
assert.deepEqual({a: 1}, linter.merge(undefined, {a: 1}));
});
- test('merge where one object has inherited properties', function () {
- var util = require('util');
+ test("merge where one object has inherited properties", function () {
+ var util = require("util");
function A() {
- this.parent = 'overridden';
+ this.parent = "overridden";
}
function B() {
- this.own = 'overridden'
+ this.own = "overridden"
}
- var c = { parent: 'orig', own: 'orig' };
+ var c = { parent: "orig", own: "orig" };
util.inherits(B, A);
- assert.deepEqual({ parent: 'orig', own: 'overridden' },
+ assert.deepEqual({ parent: "orig", own: "overridden" },
linter.merge(new B(), c));
});
});
-suite('preprocessScript', function () {
- test('removes leading BOM', function () {
+suite("preprocessScript", function () {
+ test("removes leading BOM", function () {
- assert.equal('var x=1;', linter.preprocessScript('var x=1;'));
+ assert.equal("var x=1;", linter.preprocessScript("var x=1;"));
- assert.equal('var x=1;', linter.preprocessScript('\uFEFFvar x=1;'));
+ assert.equal("var x=1;", linter.preprocessScript("\uFEFFvar x=1;"));
});
- test('removes shebang', function () {
+ test("removes shebang", function () {
- assert.equal('\nvar x=1;', linter.preprocessScript('#!/usr/bin/env node\nvar x=1;'));
+ assert.equal("\nvar x=1;", linter.preprocessScript("#!/usr/bin/env node\nvar x=1;"));
});
});
-suite('lint', function () {
+suite("lint", function () {
var oldHome = process.env.HOME;
teardown(function () {
process.env.HOME = oldHome;
});
- test('basic lint step', function () {
+ test("basic lint step", function () {
- var script = "// only a comment\n",
- options = {edition: 'latest'},
- JSLINT = nodelint.load(options.edition),
- result;
+ var script = "// only a comment\n";
+ var options = {edition: "latest"};
+ var JSLINT = nodelint.load(options.edition);
+ var result;
// don't let user's config interfere with our test
- process.env.HOME = '';
+ process.env.HOME = "";
result = linter.doLint(JSLINT, script, options);
@@ -73,15 +73,15 @@ suite('lint', function () {
assert.ok(result.ok);
});
- test('lint finds error', function () {
+ test("lint finds error", function () {
- var script = "//TODO: remove this\n",
- options = {edition: '2013-09-22'},
- JSLINT = nodelint.load(options.edition),
- result;
+ var script = "//TODO: remove this\n";
+ var options = {edition: "2013-09-22"};
+ var JSLINT = nodelint.load(options.edition);
+ var result;
// don't let user's config interfere with our test
- process.env.HOME = '';
+ process.env.HOME = "";
result = linter.doLint(JSLINT, script, options);
@@ -91,11 +91,11 @@ suite('lint', function () {
});
- test('maxerr causes null error', function () {
- var JSLINT = nodelint.load('lib/jslint-2013-09-22.js'),
- script = "var __evil = eval('3')",
- options = {maxerr: 1},
- result;
+ test("maxerr causes null error", function () {
+ var JSLINT = nodelint.load("lib/jslint-2013-09-22.js");
+ var script = "var __evil = eval("3")";
+ var options = {maxerr: 1};
+ var result;
result = linter.doLint(JSLINT, script, options);
diff --git a/test/lintstream.js b/test/lintstream.js
index 7cf7a27..a501b71 100644
--- a/test/lintstream.js
+++ b/test/lintstream.js
@@ -6,30 +6,30 @@
// Released under modified MIT/BSD 3-clause license
// See LICENSE for details.
-var assert = require('assert'),
- stream = require('../lib/stream'),
- LintStream = require('../lib/lintstream.js');
+var assert = require("assert");
+var stream = require("../lib/stream");
+var LintStream = require("../lib/lintstream.js");
-suite('lintstream', function () {
- test('can create object', function () {
+suite("lintstream", function () {
+ test("can create object", function () {
var l = new LintStream();
assert.ok(l instanceof LintStream);
assert.ok(l instanceof stream.Transform);
});
- test('can create object incorrectly', function () {
+ test("can create object incorrectly", function () {
var l = LintStream();
assert.ok(l instanceof LintStream);
assert.ok(l instanceof stream.Transform);
});
- test('can lint a file', function (done) {
+ test("can lint a file", function (done) {
var l = new LintStream();
- l.on('data', function (chunk) {
- assert.equal(chunk.file, 'example.js');
+ l.on("data", function (chunk) {
+ assert.equal(chunk.file, "example.js");
assert.ok(chunk.linted.ok);
assert.deepEqual(chunk.linted.errors, []);
@@ -38,6 +38,6 @@ suite('lintstream', function () {
done();
});
- l.write({file: 'example.js', body: ''});
+ l.write({file: "example.js", body: ""});
});
});
diff --git a/test/main.js b/test/main.js
index e57701c..a736673 100644
--- a/test/main.js
+++ b/test/main.js
@@ -1,5 +1,5 @@
-var assert = require('assert'),
- main;
+var assert = require("assert");
+var main;
function mockConsole() {
var c ={
@@ -18,7 +18,7 @@ function mockConsole() {
function mockProcess() {
var p = {
- argv: ['jslint'],
+ argv: ["jslint"],
exit: function (c) {
this.exitCode = c;
this.events.exit.forEach(function (f) {
@@ -70,11 +70,12 @@ function mockParsed() {
};
}
-suite('jslint main', function () {
- var pro, con;
+suite("jslint main", function () {
+ var pro;
+ var con;
setup(function () {
- main = require('../lib/main');
+ main = require("../lib/main");
con = mockConsole();
pro = mockProcess();
@@ -83,7 +84,7 @@ suite('jslint main', function () {
main.setProcess(pro);
});
- test('main - no args', function () {
+ test("main - no args", function () {
var parsed = mockParsed();
main.runMain(parsed);
@@ -93,21 +94,21 @@ suite('jslint main', function () {
assert.strictEqual(2, con.warnings.length);
});
- test('main - bad lint', function (done) {
+ test("main - bad lint", function (done) {
var parsed = mockParsed();
- parsed.argv.remain.push('test/fixtures/bad.js');
+ parsed.argv.remain.push("test/fixtures/bad.js");
main.runMain(parsed);
- pro.on('exit', function () {
+ pro.on("exit", function () {
assert.equal(1, pro.exitCode);
done();
});
});
- test('main - glob files', function (done) {
+ test("main - glob files", function (done) {
// bail if glob not installed
if (!main.glob) {
assert.ok(true);
@@ -117,9 +118,9 @@ suite('jslint main', function () {
var parsed = mockParsed();
- parsed.argv.remain.push('lib/mai*.js');
+ parsed.argv.remain.push("lib/mai*.js");
- pro.on('exit', done);
+ pro.on("exit", done);
parsed.terse = true;
@@ -128,15 +129,15 @@ suite('jslint main', function () {
assert.ok(main);
});
- test('main - glob ignore node_modules', function (done) {
+ test("main - glob ignore node_modules", function (done) {
var parsed = mockParsed();
- parsed.argv.remain.push('./lib/main.js');
- parsed.argv.remain.push('./node_modules/glob/*');
+ parsed.argv.remain.push("./lib/main.js");
+ parsed.argv.remain.push("./node_modules/glob/*");
parsed.filter = true;
- pro.on('exit', done);
+ pro.on("exit", done);
parsed.terse = true;
@@ -145,15 +146,15 @@ suite('jslint main', function () {
assert.ok(main);
});
- test('main - glob --no-filter includes node_modules', function (done) {
+ test("main - glob --no-filter includes node_modules", function (done) {
var parsed = mockParsed();
- parsed.argv.remain.push('./lib/main.js');
- parsed.argv.remain.push('./node_modules/glob/*.js');
+ parsed.argv.remain.push("./lib/main.js");
+ parsed.argv.remain.push("./node_modules/glob/*.js");
parsed.filter = false;
- pro.on('exit', done);
+ pro.on("exit", done);
parsed.terse = true;
@@ -162,16 +163,16 @@ suite('jslint main', function () {
assert.ok(main);
});
- test('main - one file, not tty, json output', function (done) {
+ test("main - one file, not tty, json output", function (done) {
var parsed = mockParsed();
- parsed.argv.remain.push('test/fixtures/good.js');
+ parsed.argv.remain.push("test/fixtures/good.js");
parsed.json = true;
pro.stdout.isTTY = false;
- pro.on('exit', function () {
+ pro.on("exit", function () {
assert.strictEqual(0, pro.exitCode);
done();
});
@@ -184,7 +185,7 @@ suite('jslint main', function () {
assert.strictEqual(undefined, pro.exitCode);
});
- test('todo in command-line options', function () {
+ test("todo in command-line options", function () {
var o = main.commandOptions();
assert.strictEqual(Boolean, o.todo);
@@ -194,7 +195,7 @@ suite('jslint main', function () {
return type(value).valueOf() === value;
}
- test('isBasicType works', function () {
+ test("isBasicType works", function () {
assert.ok(isBasicType(Boolean, true));
assert.ok(isBasicType(Boolean, false));
@@ -204,30 +205,30 @@ suite('jslint main', function () {
assert.ok(!isBasicType(Boolean, 1));
assert.ok(!isBasicType(Number, false));
assert.ok(!isBasicType(String, 1));
- assert.ok(!isBasicType(Number, '1'));
+ assert.ok(!isBasicType(Number, "1"));
});
- test('example jslint.conf contains only valid options', function (done) {
+ test("example jslint.conf contains only valid options", function (done) {
- var options = main.commandOptions(),
- fs = require('fs');
+ var options = main.commandOptions();
+ var fs = require("fs");
fs.readFile("jslint.conf.example", function (err, file) {
if (err) {
throw err;
}
- var example = JSON.parse(file),
- keys = Object.keys(example);
+ var example = JSON.parse(file);
+ var keys = Object.keys(example);
keys.forEach(function(opt) {
assert.ok(options.hasOwnProperty(opt));
- var type = options[opt],
- value = example[opt];
+ var type = options[opt];
+ var value = example[opt];
- // skip predef because arrays are hard
- if (opt !== "predef") {
+ // skip predef because arrays are hard
+ if (opt !== "predef") {
assert.ok(isBasicType(type, value));
}
});
@@ -236,13 +237,13 @@ suite('jslint main', function () {
});
});
- test('edition is a string (not Boolean) option', function () {
+ test("edition is a string (not Boolean) option", function () {
var options = main.commandOptions();
assert.equal(String, options.edition);
});
- test('returns a version', function (done) {
+ test("returns a version", function (done) {
main.reportVersion(function (version) {
@@ -252,50 +253,50 @@ suite('jslint main', function () {
}, {} );
});
- test('argument parsing: edition is a string', function () {
- var options = main.parseArgs(['node', 'jslint', '--edition=latest']);
+ test("argument parsing: edition is a string", function () {
+ var options = main.parseArgs(["node", "jslint", "--edition=latest"]);
- assert.equal('latest', options.edition);
+ assert.equal("latest", options.edition);
});
- test('argument parsing: can disable filter', function () {
- var options = main.parseArgs(['node', 'jslint', '--no-filter']);
+ test("argument parsing: can disable filter", function () {
+ var options = main.parseArgs(["node", "jslint", "--no-filter"]);
assert.equal(false, options.filter);
});
-
- test('main -- report version', function (done) {
+
+ test("main -- report version", function (done) {
main.runMain({version: true});
done();
});
- test('most data goes to console.log', function (done) {
+ test("most data goes to console.log", function (done) {
var rep = main.makeReporter({});
- rep.on('finish', function () {
+ rep.on("finish", function () {
assert.equal(con.loggings.length, 1);
- assert.equal(con.loggings[0], 'log message');
+ assert.equal(con.loggings[0], "log message");
done();
});
- rep.emit('data', 'log message');
+ rep.emit("data", "log message");
rep.end();
});
- test('dots go to process.stderr', function (done) {
+ test("dots go to process.stderr", function (done) {
var rep = main.makeReporter({});
- rep.on('finish', function () {
+ rep.on("finish", function () {
assert.equal(pro.stderrWritings.length, 1);
- assert.equal(pro.stderrWritings[0], '.');
+ assert.equal(pro.stderrWritings[0], ".");
done();
});
- rep.emit('data', '.');
+ rep.emit("data", ".");
rep.end();
});
- test('quasi-programmatic interface', function (done) {
+ test("quasi-programmatic interface", function (done) {
var parsed = mockParsed();
- parsed.argv.remain.push('test/fixtures/bad.js');
+ parsed.argv.remain.push("test/fixtures/bad.js");
parsed.collector = true;
main.runMain(parsed, function (err, report) {
diff --git a/test/nodelint.js b/test/nodelint.js
index 38b775e..42775b0 100644
--- a/test/nodelint.js
+++ b/test/nodelint.js
@@ -1,18 +1,18 @@
-var assert = require('assert'),
- nodelint = require('../lib/nodelint');
+var assert = require("assert");
+var nodelint = require("../lib/nodelint");
-suite('jslint loader', function () {
- test('load implicit jslint', function () {
+suite("jslint loader", function () {
+ test("load implicit jslint", function () {
var JSLINT = nodelint.load();
assert.ok(JSLINT);
});
- test('load explicit jslint', function () {
- var JSLINT = nodelint.load('latest');
+ test("load explicit jslint", function () {
+ var JSLINT = nodelint.load("latest");
assert.ok(JSLINT);
});
- test('load nonexistent jslint', function () {
+ test("load nonexistent jslint", function () {
// mock console object
var con = { warnings: [],
warn: function(str) {
@@ -21,26 +21,26 @@ suite('jslint loader', function () {
};
nodelint.setConsole(con);
- var JSLINT = nodelint.load('nonexistent');
+ var JSLINT = nodelint.load("nonexistent");
assert.ok(JSLINT);
// expect console warning
assert.strictEqual(1, con.warnings.length);
});
- test('load by filename', function () {
- var JSLINT = nodelint.load('lib/jslint-2013-09-22.js');
+ test("load by filename", function () {
+ var JSLINT = nodelint.load("lib/jslint-2013-09-22.js");
assert.ok(JSLINT);
- assert.equal('2013-09-22', JSLINT.edition);
+ assert.equal("2013-09-22", JSLINT.edition);
});
- test('looks like a filename', function () {
+ test("looks like a filename", function () {
var names = {
- 'foo': false,
- 'foo.js': true,
- 'foo/bar': true,
- 'foo\\bar': true
+ "foo": false,
+ "foo.js": true,
+ "foo/bar": true,
+ "foo\\bar": true
};
Object.keys(names).forEach(function (n) {
diff --git a/test/options.js b/test/options.js
index b6b1c4b..bf84ecc 100644
--- a/test/options.js
+++ b/test/options.js
@@ -1,39 +1,39 @@
-var assert = require('assert'),
- options = require('../lib/options');
+var assert = require("assert");
+var options = require("../lib/options");
-suite('addDefaults', function () {
- test('set node, es5 if unset', function () {
+suite("addDefaults", function () {
+ test("set node, es5 if unset", function () {
assert.deepEqual({white: false, color: true, node: true, es5: true},
options.addDefaults({white: false, color: true}));
});
- test('explicit set prevents override', function () {
+ test("explicit set prevents override", function () {
assert.deepEqual({node: false, es5: false},
options.addDefaults({es5: false, node: false}));
});
});
-suite('splitPredefs', function () {
- test('can split predefs', function () {
- assert.deepEqual({predef: ['foo', 'bar', 'baz']},
+suite("splitPredefs", function () {
+ test("can split predefs", function () {
+ assert.deepEqual({predef: ["foo", "bar", "baz"]},
options.splitPredefs({predef: "foo,bar,baz"}));
});
- test('doesnt re-split predefs', function () {
- assert.deepEqual({predef: ['foo', 'bar', 'baz']},
- options.splitPredefs({predef: ['foo', 'bar', 'baz']}));
+ test("doesnt re-split predefs", function () {
+ assert.deepEqual({predef: ["foo", "bar", "baz"]},
+ options.splitPredefs({predef: ["foo", "bar", "baz"]}));
});
});
-suite('preprocessOptions', function() {
- test('accepts falsy options', function () {
+suite("preprocessOptions", function() {
+ test("accepts falsy options", function () {
assert.deepEqual(options.preprocessOptions(undefined), options.preprocessOptions({}));
});
});
-suite('current dir config file', function () {
- var oldDir = process.cwd(),
- fs = require('fs.extra'),
- con;
+suite("current dir config file", function () {
+ var oldDir = process.cwd();
+ var fs = require("fs.extra");
+ var con;
function mockConsole() {
return {
@@ -49,77 +49,77 @@ suite('current dir config file', function () {
con = mockConsole();
options.setConsole(con);
- fs.mkdir('test_config', function (err) {
+ fs.mkdir("test_config", function (err) {
if (err) return done(err);
- process.chdir('test_config');
+ process.chdir("test_config");
done();
});
});
suiteTeardown(function (done) {
process.chdir(oldDir);
- fs.rmrf('test_config', function (err) {
+ fs.rmrf("test_config", function (err) {
if (err) return done(err);
done();
});
});
- test('read jslintrc when present', function () {
- fs.writeFileSync('jslintrc', '{"foo": 1}');
+ test("read jslintrc when present", function () {
+ fs.writeFileSync("jslintrc", "{"foo": 1}");
- assert.deepEqual({foo: 1}, options.loadAndParseConfig('jslintrc'));
+ assert.deepEqual({foo: 1}, options.loadAndParseConfig("jslintrc"));
});
- test('no crash when empty jslintrc', function () {
- fs.writeFileSync('jslintrc', '');
+ test("no crash when empty jslintrc", function () {
+ fs.writeFileSync("jslintrc", "");
- assert.deepEqual(undefined, options.loadAndParseConfig('jslintrc'));
+ assert.deepEqual(undefined, options.loadAndParseConfig("jslintrc"));
assert(/Error reading config file "jslintrc": SyntaxError: Unexpected end of /.test(con.warnings[0]));
});
- test('no crash when malformed jslintrc', function () {
- fs.writeFileSync('jslintrc', "{ 'invalid json': true");
+ test("no crash when malformed jslintrc", function () {
+ fs.writeFileSync("jslintrc", "{ "invalid json": true");
- assert.deepEqual(undefined, options.loadAndParseConfig('jslintrc'));
+ assert.deepEqual(undefined, options.loadAndParseConfig("jslintrc"));
assert(/Error reading config file "jslintrc": SyntaxError: Unexpected end of /.test(con.warnings[0]));
});
- test('nonexistent .jslintrc => undefined', function () {
- assert.deepEqual(undefined, options.loadAndParseConfig('.jslintrc'));
+ test("nonexistent .jslintrc => undefined", function () {
+ assert.deepEqual(undefined, options.loadAndParseConfig(".jslintrc"));
});
- test('merge global and local config correctly', function () {
- fs.writeFileSync('home', '{"foo": 1}');
- fs.writeFileSync('local', '{"foo": 2}');
+ test("merge global and local config correctly", function () {
+ fs.writeFileSync("home", "{"foo": 1}");
+ fs.writeFileSync("local", "{"foo": 2}");
// no local = use home
- assert.deepEqual({foo: 1}, options.mergeConfigs(['home'], []));
+ assert.deepEqual({foo: 1}, options.mergeConfigs(["home"], []));
// local overrides home
- assert.deepEqual({foo: 2}, options.mergeConfigs(['home'], ['local']));
+ assert.deepEqual({foo: 2}, options.mergeConfigs(["home"], ["local"]));
// either branch of local overrides home
- assert.deepEqual({foo: 2}, options.mergeConfigs(['home'], ['filenotfound', 'local']));
+ assert.deepEqual({foo: 2}, options.mergeConfigs(["home"], ["filenotfound", "local"]));
});
- test('load specific-named config files', function () {
- fs.writeFileSync('.jslintrc', '{"foo": "home"}');
- fs.writeFileSync('jslintrc', '{"foo": "local"}');
+ test("load specific-named config files", function () {
+ fs.writeFileSync(".jslintrc", '{"foo": "home"}');
+ fs.writeFileSync("jslintrc", '{"foo": "local"}');
// pretend current directory is home
- assert.deepEqual({foo: "local"}, options.loadConfig('.'));
+ assert.deepEqual({foo: "local"}, options.loadConfig("."));
// Windows: process.env.HOME can be unset (undefined)
assert.deepEqual({foo: "local"}, options.loadConfig(undefined));
});
- test('load user-named config files', function () {
- fs.writeFileSync('user.jslint.conf', '{"bar": "user"}');
+ test("load user-named config files", function () {
+ fs.writeFileSync("user.jslint.conf", '{"bar": "user"}');
// pretend current directory is home
- var conf = options.loadConfig('.', './user.jslint.conf');
+ var conf = options.loadConfig(".", "./user.jslint.conf");
assert.equal("user", conf.bar);
diff --git a/test/regression.js b/test/regression.js
index 94ddf74..6c7d232 100644
--- a/test/regression.js
+++ b/test/regression.js
@@ -1,42 +1,42 @@
-var assert = require('assert'),
- nodelint = require('../lib/nodelint'),
- linter = require('../lib/linter');
+var assert = require("assert");
+var nodelint = require("../lib/nodelint");
+var linter = require("../lib/linter");
-suite('case #101', function () {
+suite("case #101", function () {
// https://github.com/reid/node-jslint/issues/101
- test('has warning with default options', function () {
+ test("has warning with default options", function () {
- var options = {edition: 'latest'},
- JSLINT = nodelint.load(options.edition),
- script = "console.log('a');\n",
- result = linter.doLint(JSLINT, script, options);
+ var options = {edition: "latest"};
+ var JSLINT = nodelint.load(options.edition);
+ var script = "console.log('a');\n";
+ var result = linter.doLint(JSLINT, script, options);
assert.ok(!result.ok);
});
- test('no warning with node option', function () {
+ test("no warning with node option", function () {
- var options = {edition: 'latest', node: true},
- JSLINT = nodelint.load(options.edition),
- script = "console.log('a');\n",
- result = linter.doLint(JSLINT, script, options);
+ var options = {edition: "latest", node: true};
+ var JSLINT = nodelint.load(options.edition);
+ var script = "console.log('a');\n";
+ var result = linter.doLint(JSLINT, script, options);
assert.ok(result.ok);
});
- test('use es6 linter', function () {
+ test("use es6 linter", function () {
- var options = {edition: 'es6', node: true},
- JSLINT = nodelint.load(options.edition),
- script = "console.log('a');\n",
- result = linter.doLint(JSLINT, script, options);
+ var options = {edition: "es6", node: true};
+ var JSLINT = nodelint.load(options.edition);
+ var script = "console.log('a');\n";
+ var result = linter.doLint(JSLINT, script, options);
assert.ok(result);
});
- test('post-es6 versions report edition', function (done) {
- var options = {edition: '2015-05-08'},
- main = require('../lib/main');
+ test("post-es6 versions report edition", function (done) {
+ var options = {edition: "2015-05-08"};
+ var main = require("../lib/main");
main.reportVersion(function (version) {
assert.ok(/^node-jslint version:/.test(version));
diff --git a/test/reporter.js b/test/reporter.js
index 5be67e2..dedf5f0 100644
--- a/test/reporter.js
+++ b/test/reporter.js
@@ -1,10 +1,10 @@
-var reporter = require('../lib/reporter'),
- assert = require('assert');
+var reporter = require("../lib/reporter");
+var assert = require("assert");
-suite('reporter', function () {
- 'use strict';
- var log,
- errorLint = {
+suite("reporter", function () {
+ "use strict";
+ var log;
+ var errorLint = {
ok: false,
errors: [
err(1, 1, "Fake error 1."),
@@ -59,58 +59,58 @@ suite('reporter', function () {
reporter.setLogger(log);
});
- test('lint OK, no color, no terse', function () {
- reporter.report('example.js', {ok: true}, false, false);
+ test("lint OK, no color, no terse", function () {
+ reporter.report("example.js", {ok: true}, false, false);
assert.deepEqual(1, log.outLines.length);
assert.deepEqual("\nexample.js is OK.", log.outLines[0]);
});
- test('lint OK, no color, terse', function () {
- reporter.report('example.js', {ok: true}, false, true);
+ test("lint OK, no color, terse", function () {
+ reporter.report("example.js", {ok: true}, false, true);
assert.deepEqual(1, log.errLines.length);
assert.deepEqual(".", log.errLines[0]);
});
- test('lint OK, color, no terse', function () {
- reporter.report('example.js', {ok: true}, true, false);
+ test("lint OK, color, no terse", function () {
+ reporter.report("example.js", {ok: true}, true, false);
assert.deepEqual(1, log.outLines.length);
assert.notEqual(-1, log.outLines[0].search(/OK/));
});
- test('lint bad, no color, no terse', function () {
- reporter.report('example.js', errorLint, false, false);
+ test("lint bad, no color, no terse", function () {
+ reporter.report("example.js", errorLint, false, false);
assert.deepEqual(5, log.outLines.length);
assert.equal(-1, log.outLines[0].search(/OK/));
assert.deepEqual([
- '\nexample.js',
- ' #1 Fake error 1.',
- ' // Line 1, Pos 1',
- ' #2 Fake error 2.',
- ' Fake evidence // Line 2, Pos 3'
+ "\nexample.js",
+ " #1 Fake error 1.",
+ " // Line 1, Pos 1",
+ " #2 Fake error 2.",
+ " Fake evidence // Line 2, Pos 3"
], log.outLines);
});
- test('lint bad, no color, terse', function () {
- reporter.report('example.js', errorLint, false, true);
+ test("lint bad, no color, terse", function () {
+ reporter.report("example.js", errorLint, false, true);
assert.deepEqual(2, log.outLines.length);
assert.equal(-1, log.outLines[0].search(/OK/));
assert.deepEqual([
- 'example.js:1:1: Fake error 1.',
- 'example.js:2:3: Fake error 2.'
+ "example.js:1:1: Fake error 1.",
+ "example.js:2:3: Fake error 2."
], log.outLines);
});
- test('lint bad, color, no terse', function () {
- reporter.report('example.js', errorLint, true, false);
+ test("lint bad, color, no terse", function () {
+ reporter.report("example.js", errorLint, true, false);
assert.deepEqual(5, log.outLines.length);
assert.equal(-1, log.outLines[0].search(/OK/));
@@ -118,17 +118,17 @@ suite('reporter', function () {
// no assertion re content: just force exercise of color branch
});
- test('lint bad, es6 format', function () {
- reporter.report('example.js', es6ErrorLint, true, false);
+ test("lint bad, es6 format", function () {
+ reporter.report("example.js", es6ErrorLint, true, false);
});
- test('fudge fudges output', function (done) {
- var reporter = require('../lib/reporter.js');
+ test("fudge fudges output", function (done) {
+ var reporter = require("../lib/reporter.js");
- reporter.report('example.js', es6Fudged, false, true);
+ reporter.report("example.js", es6Fudged, false, true);
assert.deepEqual(1, log.outLines.length);
- assert.equal(log.outLines[0], 'example.js:1:4: foo');
+ assert.equal(log.outLines[0], "example.js:1:4: foo");
done();
});
diff --git a/test/reportstream.js b/test/reportstream.js
index 1ab8b8b..d6b746c 100644
--- a/test/reportstream.js
+++ b/test/reportstream.js
@@ -1,90 +1,90 @@
-var assert = require('assert'),
- stream = require('../lib/stream'),
- ReportStream = require('../lib/reportstream.js'),
- JSONReportStream = require('../lib/jsonreportstream.js'),
- CollectorStream = require('../lib/collectorstream.js');
-
-suite('reportstream', function () {
- test('can create object', function () {
+var assert = require("assert");
+var stream = require("../lib/stream");
+var ReportStream = require("../lib/reportstream.js");
+var JSONReportStream = require("../lib/jsonreportstream.js");
+var CollectorStream = require("../lib/collectorstream.js");
+
+suite("reportstream", function () {
+ test("can create object", function () {
var r = new ReportStream();
assert.ok(r instanceof ReportStream);
assert.ok(r instanceof stream.Transform);
});
- test('can create object incorrectly', function () {
+ test("can create object incorrectly", function () {
var r = ReportStream();
assert.ok(r instanceof ReportStream);
assert.ok(r instanceof stream.Transform);
});
- test('can async', function (done) {
+ test("can async", function (done) {
var r = new ReportStream();
- r.on('data', function(chunk) {
- assert.deepEqual(chunk, '\nexample.js is OK.');
+ r.on("data", function(chunk) {
+ assert.deepEqual(chunk, "\nexample.js is OK.");
done();
});
- r.write({file: 'example.js', linted: {ok: true}});
+ r.write({file: "example.js", linted: {ok: true}});
r.end();
});
- test('can make a colorized reporter', function (done) {
+ test("can make a colorized reporter", function (done) {
var r = new ReportStream({color: true});
- r.on('data', function(chunk) {
- assert.deepEqual(chunk, '\n\x1b[1mexample.js\x1b[0m is \x1b[32mOK\x1b[0m.');
+ r.on("data", function(chunk) {
+ assert.deepEqual(chunk, "\n\x1b[1mexample.js\x1b[0m is \x1b[32mOK\x1b[0m.");
done();
});
- r.write({file: 'example.js', linted: {ok: true}});
+ r.write({file: "example.js", linted: {ok: true}});
r.end();
});
- test('can make a terse reporter', function (done) {
+ test("can make a terse reporter", function (done) {
var r = new ReportStream({terse: true});
- r.on('data', function(chunk) {
- assert.deepEqual(chunk, '.');
+ r.on("data", function(chunk) {
+ assert.deepEqual(chunk, ".");
done();
});
- r.write({file: 'example.js', linted: {ok: true}});
+ r.write({file: "example.js", linted: {ok: true}});
r.end();
});
- test('can make two reporters with diff. behavior', function (done) {
- var r1 = new ReportStream(),
- r2 = new ReportStream({terse: true});
+ test("can make two reporters with diff. behavior", function (done) {
+ var r1 = new ReportStream();
+ var r2 = new ReportStream({terse: true});
- r1.on('data', function (chunk) {
- r2.write({file: 'example.js', linted: {ok: true}});
+ r1.on("data", function (chunk) {
+ r2.write({file: "example.js", linted: {ok: true}});
r2.end();
});
- r2.on('data', function (chunk) {
- assert.equal(chunk, '.');
+ r2.on("data", function (chunk) {
+ assert.equal(chunk, ".");
done();
});
- r1.write({file: 'example.js', linted: {ok: true}});
+ r1.write({file: "example.js", linted: {ok: true}});
r1.end();
});
- test('how about a JSONReportStream', function (done) {
+ test("how about a JSONReportStream", function (done) {
var r = new JSONReportStream();
- r.on('data', function (chunk) {
- assert.deepEqual(chunk, JSON.stringify(['example.js', null]));
+ r.on("data", function (chunk) {
+ assert.deepEqual(chunk, JSON.stringify(["example.js", null]));
done();
});
- r.write({file: 'example.js', linted: {ok: true}});
+ r.write({file: "example.js", linted: {ok: true}});
r.end();
});
- test('incorrectly construct a JSONReportStream', function (done) {
+ test("incorrectly construct a JSONReportStream", function (done) {
var r = JSONReportStream();
assert.ok(r instanceof JSONReportStream);
@@ -93,15 +93,15 @@ suite('reportstream', function () {
});
});
-suite('collectorstream', function () {
- test('can create object', function () {
+suite("collectorstream", function () {
+ test("can create object", function () {
var r = new CollectorStream();
assert.ok(r instanceof CollectorStream);
assert.ok(r instanceof stream.Transform);
});
- test('can create object incorrectly', function () {
+ test("can create object incorrectly", function () {
var r = CollectorStream();
assert.ok(r instanceof CollectorStream);