/jslint.js

http://github.com/douglascrockford/JSLint · JavaScript · 4988 lines · 4235 code · 382 blank · 371 comment · 1009 complexity · f4a6727d4e59e18bbf8023a841c562e3 MD5 · raw file

  1. // jslint.js
  2. // 2020-03-28
  3. // Copyright (c) 2015 Douglas Crockford (www.JSLint.com)
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. // The Software shall be used for Good, not Evil.
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. // SOFTWARE.
  20. // jslint(source, option_object, global_array) is a function that takes 3
  21. // arguments. The second two arguments are optional.
  22. // source A text to analyze, a string or an array of strings.
  23. // option_object An object whose keys correspond to option names.
  24. // global_array An array of strings containing global variables that
  25. // the file is allowed readonly access.
  26. // jslint returns an object containing its results. The object contains a lot
  27. // of valuable information. It can be used to generate reports. The object
  28. // contains:
  29. // directives: an array of directive comment tokens.
  30. // edition: the version of JSLint that did the analysis.
  31. // exports: the names exported from the module.
  32. // froms: an array of strings representing each of the imports.
  33. // functions: an array of objects that represent all of the functions
  34. // declared in the file.
  35. // global: an object representing the global object. Its .context property
  36. // is an object containing a property for each global variable.
  37. // id: "(JSLint)"
  38. // json: true if the file is a JSON text.
  39. // lines: an array of strings, the source.
  40. // module: true if an import or export statement was used.
  41. // ok: true if no warnings were generated. This is what you want.
  42. // option: the option argument.
  43. // property: a property object.
  44. // stop: true if JSLint was unable to finish. You don't want this.
  45. // tokens: an array of objects representing the tokens in the file.
  46. // tree: the token objects arranged in a tree.
  47. // warnings: an array of warning objects. A warning object can contain:
  48. // name: "JSLintError"
  49. // column: A column number in the file.
  50. // line: A line number in the file.
  51. // code: A warning code string.
  52. // message: The warning message string.
  53. // a: Exhibit A.
  54. // b: Exhibit B.
  55. // c: Exhibit C.
  56. // d: Exhibit D.
  57. // jslint works in several phases. In any of these phases, errors might be
  58. // found. Sometimes JSLint is able to recover from an error and continue
  59. // parsing. In some cases, it cannot and will stop early. If that should happen,
  60. // repair your code and try again.
  61. // Phases:
  62. // 1. If the source is a single string, split it into an array of strings.
  63. // 2. Turn the source into an array of tokens.
  64. // 3. Furcate the tokens into a parse tree.
  65. // 4. Walk the tree, traversing all of the nodes of the tree. It is a
  66. // recursive traversal. Each node may be processed on the way down
  67. // (preaction) and on the way up (postaction).
  68. // 5. Check the whitespace between the tokens.
  69. // jslint can also examine JSON text. It decides that a file is JSON text if
  70. // the first token is "[" or "{". Processing of JSON text is much simpler than
  71. // the processing of JavaScript programs. Only the first three phases are
  72. // required.
  73. // WARNING: JSLint will hurt your feelings.
  74. /*property
  75. a, and, arity, assign, b, bad_assignment_a, bad_directive_a, bad_get,
  76. bad_module_name_a, bad_option_a, bad_property_a, bad_set, bitwise, block,
  77. body, browser, c, calls, catch, charCodeAt, closer, closure, code, column,
  78. concat, constant, context, convert, couch, create, d, dead, default, devel,
  79. directive, directives, disrupt, dot, duplicate_a, edition, ellipsis, else,
  80. empty_block, escape_mega, eval, every, expected_a, expected_a_at_b_c,
  81. expected_a_b, expected_a_b_from_c_d, expected_a_before_b,
  82. expected_a_next_at_b, expected_digits_after_a, expected_four_digits,
  83. expected_identifier_a, expected_line_break_a_b, expected_regexp_factor_a,
  84. expected_space_a_b, expected_statements_a, expected_string_a,
  85. expected_type_string_a, exports, expression, extra, finally, flag, for,
  86. forEach, free, freeze, freeze_exports, from, froms, fud, fudge,
  87. function_in_loop, functions, g, getset, global, i, id, identifier, import,
  88. inc, indexOf, infix_in, init, initial, isArray, isNaN, join, json, keys,
  89. label, label_a, lbp, led, length, level, line, lines, live, long, loop, m,
  90. margin, match, message, misplaced_a, misplaced_directive_a, missing_browser,
  91. missing_m, module, naked_block, name, names, nested_comment, new, node,
  92. not_label_a, nr, nud, number_isNaN, ok, open, opening, option,
  93. out_of_scope_a, parameters, parent, pop, property, push, quote,
  94. redefinition_a_b, replace, required_a_optional_b, reserved_a, role, search,
  95. shebang, signature, single, slice, some, sort, split, startsWith, statement,
  96. stop, subscript_a, switch, test, this, thru, toString, todo_comment,
  97. tokens, too_long, too_many_digits, tree, try, type, u, unclosed_comment,
  98. unclosed_mega, unclosed_string, undeclared_a, unexpected_a,
  99. unexpected_a_after_b, unexpected_a_before_b, unexpected_at_top_level_a,
  100. unexpected_char_a, unexpected_comment, unexpected_directive_a,
  101. unexpected_expression_a, unexpected_label_a, unexpected_parens,
  102. unexpected_space_a_b, unexpected_statement_a, unexpected_trailing_space,
  103. unexpected_typeof_a, uninitialized_a, unreachable_a,
  104. unregistered_property_a, unsafe, unused_a, use_double, use_open, use_spaces,
  105. used, value, var_loop, var_switch, variable, warning, warnings,
  106. weird_condition_a, weird_expression_a, weird_loop, weird_relation_a, white,
  107. wrap_condition, wrap_immediate, wrap_parameter, wrap_regexp, wrap_unary,
  108. wrapped, writable, y
  109. */
  110. function empty() {
  111. // The empty function produces a new empty object that inherits nothing. This is
  112. // much better than '{}' because confusions around accidental method names like
  113. // 'constructor' are completely avoided.
  114. return Object.create(null);
  115. }
  116. function populate(array, object = empty(), value = true) {
  117. // Augment an object by taking property names from an array of strings.
  118. array.forEach(function (name) {
  119. object[name] = value;
  120. });
  121. return object;
  122. }
  123. const allowed_option = {
  124. // These are the options that are recognized in the option object or that may
  125. // appear in a /*jslint*/ directive. Most options will have a boolean value,
  126. // usually true. Some options will also predefine some number of global
  127. // variables.
  128. bitwise: true,
  129. browser: [
  130. "caches", "CharacterData", "clearInterval", "clearTimeout", "document",
  131. "DocumentType", "DOMException", "Element", "Event", "event", "fetch",
  132. "FileReader", "FontFace", "FormData", "history", "IntersectionObserver",
  133. "localStorage", "location", "MutationObserver", "name", "navigator",
  134. "screen", "sessionStorage", "setInterval", "setTimeout", "Storage",
  135. "TextDecoder", "TextEncoder", "URL", "window", "Worker",
  136. "XMLHttpRequest"
  137. ],
  138. couch: [
  139. "emit", "getRow", "isArray", "log", "provides", "registerType",
  140. "require", "send", "start", "sum", "toJSON"
  141. ],
  142. convert: true,
  143. devel: [
  144. "alert", "confirm", "console", "prompt"
  145. ],
  146. eval: true,
  147. for: true,
  148. fudge: true,
  149. getset: true,
  150. long: true,
  151. node: [
  152. "Buffer", "clearImmediate", "clearInterval", "clearTimeout",
  153. "console", "exports", "module", "process", "require",
  154. "setImmediate", "setInterval", "setTimeout", "TextDecoder",
  155. "TextEncoder", "URL", "URLSearchParams", "__dirname", "__filename"
  156. ],
  157. single: true,
  158. this: true,
  159. white: true
  160. };
  161. const anticondition = populate([
  162. "?", "~", "&", "|", "^", "<<", ">>", ">>>", "+", "-", "*", "/", "%",
  163. "typeof", "(number)", "(string)"
  164. ]);
  165. // These are the bitwise operators.
  166. const bitwiseop = populate([
  167. "~", "^", "^=", "&", "&=", "|", "|=", "<<", "<<=", ">>", ">>=",
  168. ">>>", ">>>="
  169. ]);
  170. const escapeable = populate([
  171. "\\", "/", "`", "b", "f", "n", "r", "t"
  172. ]);
  173. const opener = {
  174. // The open and close pairs.
  175. "(": ")", // paren
  176. "[": "]", // bracket
  177. "{": "}", // brace
  178. "${": "}" // mega
  179. };
  180. // The relational operators.
  181. const relationop = populate([
  182. "!=", "!==", "==", "===", "<", "<=", ">", ">="
  183. ]);
  184. // This is the set of infix operators that require a space on each side.
  185. const spaceop = populate([
  186. "!=", "!==", "%", "%=", "&", "&=", "&&", "*", "*=", "+=", "-=", "/",
  187. "/=", "<", "<=", "<<", "<<=", "=", "==", "===", "=>", ">", ">=",
  188. ">>", ">>=", ">>>", ">>>=", "^", "^=", "|", "|=", "||"
  189. ]);
  190. const standard = [
  191. // These are the globals that are provided by the language standard.
  192. "Array", "ArrayBuffer", "Boolean", "DataView", "Date", "decodeURI",
  193. "decodeURIComponent", "encodeURI", "encodeURIComponent", "Error",
  194. "EvalError", "Float32Array", "Float64Array", "Generator",
  195. "GeneratorFunction", "Int8Array", "Int16Array", "Int32Array", "Intl",
  196. "JSON", "Map", "Math", "Number", "Object", "parseInt", "parseFloat",
  197. "Promise", "Proxy", "RangeError", "ReferenceError", "Reflect", "RegExp",
  198. "Set", "String", "Symbol", "SyntaxError", "System", "TypeError",
  199. "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array",
  200. "URIError", "WeakMap", "WeakSet"
  201. ];
  202. const bundle = {
  203. // The bundle contains the raw text messages that are generated by jslint. It
  204. // seems that they are all error messages and warnings. There are no "Atta
  205. // boy!" or "You are so awesome!" messages. There is no positive reinforcement
  206. // or encouragement. This relentless negativity can undermine self-esteem and
  207. // wound the inner child. But if you accept it as sound advice rather than as
  208. // personal criticism, it can make your programs better.
  209. and: "The '&&' subexpression should be wrapped in parens.",
  210. bad_assignment_a: "Bad assignment to '{a}'.",
  211. bad_directive_a: "Bad directive '{a}'.",
  212. bad_get: "A get function takes no parameters.",
  213. bad_module_name_a: "Bad module name '{a}'.",
  214. bad_option_a: "Bad option '{a}'.",
  215. bad_property_a: "Bad property name '{a}'.",
  216. bad_set: "A set function takes one parameter.",
  217. duplicate_a: "Duplicate '{a}'.",
  218. empty_block: "Empty block.",
  219. escape_mega: "Unexpected escapement in mega literal.",
  220. expected_a: "Expected '{a}'.",
  221. expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
  222. expected_a_b: "Expected '{a}' and instead saw '{b}'.",
  223. expected_a_b_from_c_d: (
  224. "Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'."
  225. ),
  226. expected_a_before_b: "Expected '{a}' before '{b}'.",
  227. expected_a_next_at_b: "Expected '{a}' at column {b} on the next line.",
  228. expected_digits_after_a: "Expected digits after '{a}'.",
  229. expected_four_digits: "Expected four digits after '\\u'.",
  230. expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
  231. expected_line_break_a_b: "Expected a line break between '{a}' and '{b}'.",
  232. expected_regexp_factor_a: "Expected a regexp factor and instead saw '{a}'.",
  233. expected_space_a_b: "Expected one space between '{a}' and '{b}'.",
  234. expected_statements_a: "Expected statements before '{a}'.",
  235. expected_string_a: "Expected a string and instead saw '{a}'.",
  236. expected_type_string_a: "Expected a type string and instead saw '{a}'.",
  237. freeze_exports: (
  238. "Expected 'Object.freeze('. All export values should be frozen."
  239. ),
  240. function_in_loop: "Don't make functions within a loop.",
  241. infix_in: (
  242. "Unexpected 'in'. Compare with undefined, "
  243. + "or use the hasOwnProperty method instead."
  244. ),
  245. label_a: "'{a}' is a statement label.",
  246. misplaced_a: "Place '{a}' at the outermost level.",
  247. misplaced_directive_a: (
  248. "Place the '/*{a}*/' directive before the first statement."
  249. ),
  250. missing_browser: "/*global*/ requires the Assume a browser option.",
  251. missing_m: "Expected 'm' flag on a multiline regular expression.",
  252. naked_block: "Naked block.",
  253. nested_comment: "Nested comment.",
  254. not_label_a: "'{a}' is not a label.",
  255. number_isNaN: "Use Number.isNaN function to compare with NaN.",
  256. out_of_scope_a: "'{a}' is out of scope.",
  257. redefinition_a_b: "Redefinition of '{a}' from line {b}.",
  258. required_a_optional_b: (
  259. "Required parameter '{a}' after optional parameter '{b}'."
  260. ),
  261. reserved_a: "Reserved name '{a}'.",
  262. subscript_a: "['{a}'] is better written in dot notation.",
  263. todo_comment: "Unexpected TODO comment.",
  264. too_long: "Line is longer than 80 characters.",
  265. too_many_digits: "Too many digits.",
  266. unclosed_comment: "Unclosed comment.",
  267. unclosed_mega: "Unclosed mega literal.",
  268. unclosed_string: "Unclosed string.",
  269. undeclared_a: "Undeclared '{a}'.",
  270. unexpected_a: "Unexpected '{a}'.",
  271. unexpected_a_after_b: "Unexpected '{a}' after '{b}'.",
  272. unexpected_a_before_b: "Unexpected '{a}' before '{b}'.",
  273. unexpected_at_top_level_a: "Expected '{a}' to be in a function.",
  274. unexpected_char_a: "Unexpected character '{a}'.",
  275. unexpected_comment: "Unexpected comment.",
  276. unexpected_directive_a: "When using modules, don't use directive '/*{a}'.",
  277. unexpected_expression_a: (
  278. "Unexpected expression '{a}' in statement position."
  279. ),
  280. unexpected_label_a: "Unexpected label '{a}'.",
  281. unexpected_parens: "Don't wrap function literals in parens.",
  282. unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
  283. unexpected_statement_a: (
  284. "Unexpected statement '{a}' in expression position."
  285. ),
  286. unexpected_trailing_space: "Unexpected trailing space.",
  287. unexpected_typeof_a: (
  288. "Unexpected 'typeof'. Use '===' to compare directly with {a}."
  289. ),
  290. uninitialized_a: "Uninitialized '{a}'.",
  291. unreachable_a: "Unreachable '{a}'.",
  292. unregistered_property_a: "Unregistered property name '{a}'.",
  293. unsafe: "Unsafe character '{a}'.",
  294. unused_a: "Unused '{a}'.",
  295. use_double: "Use double quotes, not single quotes.",
  296. use_open: (
  297. "Wrap a ternary expression in parens, "
  298. + "with a line break after the left paren."
  299. ),
  300. use_spaces: "Use spaces, not tabs.",
  301. var_loop: "Don't declare variables in a loop.",
  302. var_switch: "Don't declare variables in a switch.",
  303. weird_condition_a: "Weird condition '{a}'.",
  304. weird_expression_a: "Weird expression '{a}'.",
  305. weird_loop: "Weird loop.",
  306. weird_relation_a: "Weird relation '{a}'.",
  307. wrap_condition: "Wrap the condition in parens.",
  308. wrap_immediate: (
  309. "Wrap an immediate function invocation in parentheses to assist "
  310. + "the reader in understanding that the expression is the result "
  311. + "of a function, and not the function itself."
  312. ),
  313. wrap_parameter: "Wrap the parameter in parens.",
  314. wrap_regexp: "Wrap this regexp in parens to avoid confusion.",
  315. wrap_unary: "Wrap the unary expression in parens."
  316. };
  317. // Regular expression literals:
  318. // supplant {variables}
  319. const rx_supplant = /\{([^{}]*)\}/g;
  320. // carriage return, carriage return linefeed, or linefeed
  321. const rx_crlf = /\n|\r\n?/;
  322. // unsafe characters that are silently deleted by one or more browsers
  323. const rx_unsafe = /[\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/;
  324. // identifier
  325. const rx_identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;
  326. const rx_module = /^[a-zA-Z0-9_$:.@\-\/]+$/;
  327. const rx_bad_property = /^_|\$|Sync\$|_$/;
  328. // star slash
  329. const rx_star_slash = /\*\//;
  330. // slash star
  331. const rx_slash_star = /\/\*/;
  332. // slash star or ending slash
  333. const rx_slash_star_or_slash = /\/\*|\/$/;
  334. // uncompleted work comment
  335. const rx_todo = /\b(?:todo|TO\s?DO|HACK)\b/;
  336. // tab
  337. const rx_tab = /\t/g;
  338. // directive
  339. const rx_directive = /^(jslint|property|global)\s+(.*)$/;
  340. const rx_directive_part = /^([a-zA-Z$_][a-zA-Z0-9$_]*)(?::\s*(true|false))?,?\s*(.*)$/;
  341. // token (sorry it is so long)
  342. const rx_token = /^((\s+)|([a-zA-Z_$][a-zA-Z0-9_$]*)|[(){}\[\],:;'"~`]|\?\.?|=(?:==?|>)?|\.+|[*\/][*\/=]?|\+[=+]?|-[=\-]?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<<?=?|!(?:!|==?)?|(0|[1-9][0-9]*))(.*)$/;
  343. const rx_digits = /^([0-9]+)(.*)$/;
  344. const rx_hexs = /^([0-9a-fA-F]+)(.*)$/;
  345. const rx_octals = /^([0-7]+)(.*)$/;
  346. const rx_bits = /^([01]+)(.*)$/;
  347. // mega
  348. const rx_mega = /[`\\]|\$\{/;
  349. // JSON number
  350. const rx_JSON_number = /^-?\d+(?:\.\d*)?(?:e[\-+]?\d+)?$/i;
  351. // initial cap
  352. const rx_cap = /^[A-Z]/;
  353. function is_letter(string) {
  354. return (
  355. (string >= "a" && string <= "z\uffff")
  356. || (string >= "A" && string <= "Z\uffff")
  357. );
  358. }
  359. function supplant(string, object) {
  360. return string.replace(rx_supplant, function (found, filling) {
  361. const replacement = object[filling];
  362. return (
  363. replacement !== undefined
  364. ? replacement
  365. : found
  366. );
  367. });
  368. }
  369. let anon; // The guessed name for anonymous functions.
  370. let blockage; // The current block.
  371. let block_stack; // The stack of blocks.
  372. let declared_globals; // The object containing the global declarations.
  373. let directives; // The directive comments.
  374. let directive_mode; // true if directives are still allowed.
  375. let early_stop; // true if JSLint cannot finish.
  376. let exports; // The exported names and values.
  377. let froms; // The array collecting all import-from strings.
  378. let fudge; // true if the natural numbers start with 1.
  379. let functionage; // The current function.
  380. let functions; // The array containing all of the functions.
  381. let global; // The global object; the outermost context.
  382. let json_mode; // true if parsing JSON.
  383. let lines; // The array containing source lines.
  384. let mega_mode; // true if currently parsing a megastring literal.
  385. let module_mode; // true if import or export was used.
  386. let next_token; // The next token to be examined in the parse.
  387. let option; // The options parameter.
  388. let property; // The object containing the tallied property names.
  389. let shebang; // true if a #! was seen on the first line.
  390. let stack; // The stack of functions.
  391. let syntax; // The object containing the parser.
  392. let token; // The current token being examined in the parse.
  393. let token_nr; // The number of the next token.
  394. let tokens; // The array of tokens.
  395. let tenure; // The predefined property registry.
  396. let tree; // The abstract parse tree.
  397. let var_mode; // "var" if using var; "let" if using let.
  398. let warnings; // The array collecting all generated warnings.
  399. // Error reportage functions:
  400. function artifact(the_token) {
  401. // Return a string representing an artifact.
  402. if (the_token === undefined) {
  403. the_token = next_token;
  404. }
  405. return (
  406. (the_token.id === "(string)" || the_token.id === "(number)")
  407. ? String(the_token.value)
  408. : the_token.id
  409. );
  410. }
  411. function artifact_line(the_token) {
  412. // Return the fudged line number of an artifact.
  413. if (the_token === undefined) {
  414. the_token = next_token;
  415. }
  416. return the_token.line + fudge;
  417. }
  418. function artifact_column(the_token) {
  419. // Return the fudged column number of an artifact.
  420. if (the_token === undefined) {
  421. the_token = next_token;
  422. }
  423. return the_token.from + fudge;
  424. }
  425. function warn_at(code, line, column, a, b, c, d) {
  426. // Report an error at some line and column of the program. The warning object
  427. // resembles an exception.
  428. const warning = { // ~~
  429. name: "JSLintError",
  430. column,
  431. line,
  432. code
  433. };
  434. if (a !== undefined) {
  435. warning.a = a;
  436. }
  437. if (b !== undefined) {
  438. warning.b = b;
  439. }
  440. if (c !== undefined) {
  441. warning.c = c;
  442. }
  443. if (d !== undefined) {
  444. warning.d = d;
  445. }
  446. warning.message = supplant(bundle[code] || code, warning);
  447. warnings.push(warning);
  448. return warning;
  449. }
  450. function stop_at(code, line, column, a, b, c, d) {
  451. // Same as warn_at, except that it stops the analysis.
  452. throw warn_at(code, line, column, a, b, c, d);
  453. }
  454. function warn(code, the_token, a, b, c, d) {
  455. // Same as warn_at, except the warning will be associated with a specific token.
  456. // If there is already a warning on this token, suppress the new one. It is
  457. // likely that the first warning will be the most meaningful.
  458. if (the_token === undefined) {
  459. the_token = next_token;
  460. }
  461. if (the_token.warning === undefined) {
  462. the_token.warning = warn_at(
  463. code,
  464. the_token.line,
  465. the_token.from,
  466. a || artifact(the_token),
  467. b,
  468. c,
  469. d
  470. );
  471. return the_token.warning;
  472. }
  473. }
  474. function stop(code, the_token, a, b, c, d) {
  475. // Similar to warn and stop_at. If the token already had a warning, that
  476. // warning will be replaced with this new one. It is likely that the stopping
  477. // warning will be the more meaningful.
  478. if (the_token === undefined) {
  479. the_token = next_token;
  480. }
  481. delete the_token.warning;
  482. throw warn(code, the_token, a, b, c, d);
  483. }
  484. // Tokenize:
  485. function tokenize(source) {
  486. // tokenize takes a source and produces from it an array of token objects.
  487. // JavaScript is notoriously difficult to tokenize because of the horrible
  488. // interactions between automatic semicolon insertion, regular expression
  489. // literals, and now megastring literals. JSLint benefits from eliminating
  490. // automatic semicolon insertion and nested megastring literals, which allows
  491. // full tokenization to precede parsing.
  492. // If the source is not an array, then it is split into lines at the
  493. // carriage return/linefeed.
  494. lines = (
  495. Array.isArray(source)
  496. ? source
  497. : source.split(rx_crlf)
  498. );
  499. tokens = [];
  500. let char; // a popular character
  501. let column = 0; // the column number of the next character
  502. let first; // the first token
  503. let from; // the starting column number of the token
  504. let line = -1; // the line number of the next character
  505. let nr = 0; // the next token number
  506. let previous = global; // the previous token including comments
  507. let prior = global; // the previous token excluding comments
  508. let mega_from; // the starting column of megastring
  509. let mega_line; // the starting line of megastring
  510. let regexp_seen; // regular expression literal seen on this line
  511. let snippet; // a piece of string
  512. let source_line = ""; // the remaining line source string
  513. let whole_line = ""; // the whole line source string
  514. if (lines[0].startsWith("#!")) {
  515. line = 0;
  516. shebang = true;
  517. }
  518. function next_line() {
  519. // Put the next line of source in source_line. If the line contains tabs,
  520. // replace them with spaces and give a warning. Also warn if the line contains
  521. // unsafe characters or is too damn long.
  522. let at;
  523. if (
  524. !option.long
  525. && whole_line.length > 80
  526. && !json_mode
  527. && first
  528. && !regexp_seen
  529. ) {
  530. warn_at("too_long", line, 80);
  531. }
  532. column = 0;
  533. line += 1;
  534. regexp_seen = false;
  535. source_line = lines[line];
  536. whole_line = source_line || "";
  537. if (source_line !== undefined) {
  538. at = source_line.search(rx_tab);
  539. if (at >= 0) {
  540. if (!option.white) {
  541. warn_at("use_spaces", line, at + 1);
  542. }
  543. source_line = source_line.replace(rx_tab, " ");
  544. }
  545. at = source_line.search(rx_unsafe);
  546. if (at >= 0) {
  547. warn_at(
  548. "unsafe",
  549. line,
  550. column + at,
  551. "U+" + source_line.charCodeAt(at).toString(16)
  552. );
  553. }
  554. if (!option.white && source_line.slice(-1) === " ") {
  555. warn_at(
  556. "unexpected_trailing_space",
  557. line,
  558. source_line.length - 1
  559. );
  560. }
  561. }
  562. return source_line;
  563. }
  564. // Most tokens, including the identifiers, operators, and punctuators, can be
  565. // found with a regular expression. Regular expressions cannot correctly match
  566. // regular expression literals, so we will match those the hard way. String
  567. // literals and number literals can be matched by regular expressions, but they
  568. // don't provide good warnings. The functions snip, next_char, prev_char,
  569. // some_digits, and escape help in the parsing of literals.
  570. function snip() {
  571. // Remove the last character from snippet.
  572. snippet = snippet.slice(0, -1);
  573. }
  574. function next_char(match) {
  575. // Get the next character from the source line. Remove it from the source_line,
  576. // and append it to the snippet. Optionally check that the previous character
  577. // matched an expected value.
  578. if (match !== undefined && char !== match) {
  579. return stop_at(
  580. (
  581. char === ""
  582. ? "expected_a"
  583. : "expected_a_b"
  584. ),
  585. line,
  586. column - 1,
  587. match,
  588. char
  589. );
  590. }
  591. if (source_line) {
  592. char = source_line[0];
  593. source_line = source_line.slice(1);
  594. snippet += char;
  595. } else {
  596. char = "";
  597. snippet += " ";
  598. }
  599. column += 1;
  600. return char;
  601. }
  602. function back_char() {
  603. // Back up one character by moving a character from the end of the snippet to
  604. // the front of the source_line.
  605. if (snippet) {
  606. char = snippet.slice(-1);
  607. source_line = char + source_line;
  608. column -= 1;
  609. snip();
  610. } else {
  611. char = "";
  612. }
  613. return char;
  614. }
  615. function some_digits(rx, quiet) {
  616. const result = source_line.match(rx);
  617. if (result) {
  618. char = result[1];
  619. column += char.length;
  620. source_line = result[2];
  621. snippet += char;
  622. } else {
  623. char = "";
  624. if (!quiet) {
  625. warn_at(
  626. "expected_digits_after_a",
  627. line,
  628. column,
  629. snippet
  630. );
  631. }
  632. }
  633. return char.length;
  634. }
  635. function escape(extra) {
  636. next_char("\\");
  637. if (escapeable[char] === true) {
  638. return next_char();
  639. }
  640. if (char === "") {
  641. return stop_at("unclosed_string", line, column);
  642. }
  643. if (char === "u") {
  644. if (next_char("u") === "{") {
  645. if (json_mode) {
  646. warn_at("unexpected_a", line, column - 1, char);
  647. }
  648. if (some_digits(rx_hexs) > 5) {
  649. warn_at("too_many_digits", line, column - 1);
  650. }
  651. if (next_char() !== "}") {
  652. stop_at("expected_a_before_b", line, column, "}", char);
  653. }
  654. return next_char();
  655. }
  656. back_char();
  657. if (some_digits(rx_hexs, true) < 4) {
  658. warn_at("expected_four_digits", line, column - 1);
  659. }
  660. return;
  661. }
  662. if (extra && extra.indexOf(char) >= 0) {
  663. return next_char();
  664. }
  665. warn_at("unexpected_a_before_b", line, column - 2, "\\", char);
  666. }
  667. function make(id, value, identifier) {
  668. // Make the token object and append it to the tokens list.
  669. const the_token = {
  670. from,
  671. id,
  672. identifier: Boolean(identifier),
  673. line,
  674. nr,
  675. thru: column
  676. };
  677. tokens[nr] = the_token;
  678. nr += 1;
  679. // Directives must appear before the first statement.
  680. if (id !== "(comment)" && id !== ";") {
  681. directive_mode = false;
  682. }
  683. // If the token is to have a value, give it one.
  684. if (value !== undefined) {
  685. the_token.value = value;
  686. }
  687. // If this token is an identifier that touches a preceding number, or
  688. // a "/", comment, or regular expression literal that touches a preceding
  689. // comment or regular expression literal, then give a missing space warning.
  690. // This warning is not suppressed by option.white.
  691. if (
  692. previous.line === line
  693. && previous.thru === from
  694. && (id === "(comment)" || id === "(regexp)" || id === "/")
  695. && (previous.id === "(comment)" || previous.id === "(regexp)")
  696. ) {
  697. warn(
  698. "expected_space_a_b",
  699. the_token,
  700. artifact(previous),
  701. artifact(the_token)
  702. );
  703. }
  704. if (previous.id === "." && id === "(number)") {
  705. warn("expected_a_before_b", previous, "0", ".");
  706. }
  707. if (prior.id === "." && the_token.identifier) {
  708. the_token.dot = true;
  709. }
  710. // The previous token is used to detect adjacency problems.
  711. previous = the_token;
  712. // The prior token is a previous token that was not a comment. The prior token
  713. // is used to disambiguate "/", which can mean division or regular expression
  714. // literal.
  715. if (previous.id !== "(comment)") {
  716. prior = previous;
  717. }
  718. return the_token;
  719. }
  720. function parse_directive(the_comment, body) {
  721. // JSLint recognizes three directives that can be encoded in comments. This
  722. // function processes one item, and calls itself recursively to process the
  723. // next one.
  724. const result = body.match(rx_directive_part);
  725. if (result) {
  726. let allowed;
  727. const name = result[1];
  728. const value = result[2];
  729. if (the_comment.directive === "jslint") {
  730. allowed = allowed_option[name];
  731. if (
  732. typeof allowed === "boolean"
  733. || typeof allowed === "object"
  734. ) {
  735. if (
  736. value === ""
  737. || value === "true"
  738. || value === undefined
  739. ) {
  740. option[name] = true;
  741. if (Array.isArray(allowed)) {
  742. populate(allowed, declared_globals, false);
  743. }
  744. } else if (value === "false") {
  745. option[name] = false;
  746. } else {
  747. warn("bad_option_a", the_comment, name + ":" + value);
  748. }
  749. } else {
  750. warn("bad_option_a", the_comment, name);
  751. }
  752. } else if (the_comment.directive === "property") {
  753. if (tenure === undefined) {
  754. tenure = empty();
  755. }
  756. tenure[name] = true;
  757. } else if (the_comment.directive === "global") {
  758. if (value) {
  759. warn("bad_option_a", the_comment, name + ":" + value);
  760. }
  761. declared_globals[name] = false;
  762. module_mode = the_comment;
  763. }
  764. return parse_directive(the_comment, result[3]);
  765. }
  766. if (body) {
  767. return stop("bad_directive_a", the_comment, body);
  768. }
  769. }
  770. function comment(snippet) {
  771. // Make a comment object. Comments are not allowed in JSON text. Comments can
  772. // include directives and notices of incompletion.
  773. const the_comment = make("(comment)", snippet);
  774. if (Array.isArray(snippet)) {
  775. snippet = snippet.join(" ");
  776. }
  777. if (!option.devel && rx_todo.test(snippet)) {
  778. warn("todo_comment", the_comment);
  779. }
  780. const result = snippet.match(rx_directive);
  781. if (result) {
  782. if (!directive_mode) {
  783. warn_at("misplaced_directive_a", line, from, result[1]);
  784. } else {
  785. the_comment.directive = result[1];
  786. parse_directive(the_comment, result[2]);
  787. }
  788. directives.push(the_comment);
  789. }
  790. return the_comment;
  791. }
  792. function regexp() {
  793. // Parse a regular expression literal.
  794. let multi_mode = false;
  795. let result;
  796. let value;
  797. regexp_seen = true;
  798. function quantifier() {
  799. // Match an optional quantifier.
  800. if (char === "?" || char === "*" || char === "+") {
  801. next_char();
  802. } else if (char === "{") {
  803. if (some_digits(rx_digits, true) === 0) {
  804. warn_at("expected_a", line, column, "0");
  805. }
  806. if (next_char() === ",") {
  807. some_digits(rx_digits, true);
  808. next_char();
  809. }
  810. next_char("}");
  811. } else {
  812. return;
  813. }
  814. if (char === "?") {
  815. next_char("?");
  816. }
  817. }
  818. function subklass() {
  819. // Match a character in a character class.
  820. if (char === "\\") {
  821. escape("BbDdSsWw-[]^");
  822. return true;
  823. }
  824. if (
  825. char === ""
  826. || char === "["
  827. || char === "]"
  828. || char === "/"
  829. || char === "^"
  830. || char === "-"
  831. ) {
  832. return false;
  833. }
  834. if (char === " ") {
  835. warn_at("expected_a_b", line, column, "\\u0020", " ");
  836. } else if (char === "`" && mega_mode) {
  837. warn_at("unexpected_a", line, column, "`");
  838. }
  839. next_char();
  840. return true;
  841. }
  842. function ranges() {
  843. // Match a range of subclasses.
  844. if (subklass()) {
  845. if (char === "-") {
  846. next_char("-");
  847. if (!subklass()) {
  848. return stop_at(
  849. "unexpected_a",
  850. line,
  851. column - 1,
  852. "-"
  853. );
  854. }
  855. }
  856. return ranges();
  857. }
  858. }
  859. function klass() {
  860. // Match a class.
  861. next_char("[");
  862. if (char === "^") {
  863. next_char("^");
  864. }
  865. (function classy() {
  866. ranges();
  867. if (char !== "]" && char !== "") {
  868. warn_at(
  869. "expected_a_before_b",
  870. line,
  871. column,
  872. "\\",
  873. char
  874. );
  875. next_char();
  876. return classy();
  877. }
  878. }());
  879. next_char("]");
  880. }
  881. function choice() {
  882. function group() {
  883. // Match a group that starts with left paren.
  884. next_char("(");
  885. if (char === "?") {
  886. next_char("?");
  887. if (char === "=" || char === "!") {
  888. next_char();
  889. } else {
  890. next_char(":");
  891. }
  892. } else if (char === ":") {
  893. warn_at("expected_a_before_b", line, column, "?", ":");
  894. }
  895. choice();
  896. next_char(")");
  897. }
  898. function factor() {
  899. if (
  900. char === ""
  901. || char === "/"
  902. || char === "]"
  903. || char === ")"
  904. ) {
  905. return false;
  906. }
  907. if (char === "(") {
  908. group();
  909. return true;
  910. }
  911. if (char === "[") {
  912. klass();
  913. return true;
  914. }
  915. if (char === "\\") {
  916. escape("BbDdSsWw^${}[]():=!.|*+?");
  917. return true;
  918. }
  919. if (
  920. char === "?"
  921. || char === "+"
  922. || char === "*"
  923. || char === "}"
  924. || char === "{"
  925. ) {
  926. warn_at(
  927. "expected_a_before_b",
  928. line,
  929. column - 1,
  930. "\\",
  931. char
  932. );
  933. } else if (char === "`") {
  934. if (mega_mode) {
  935. warn_at("unexpected_a", line, column - 1, "`");
  936. }
  937. } else if (char === " ") {
  938. warn_at(
  939. "expected_a_b",
  940. line,
  941. column - 1,
  942. "\\s",
  943. " "
  944. );
  945. } else if (char === "$") {
  946. if (source_line[0] !== "/") {
  947. multi_mode = true;
  948. }
  949. } else if (char === "^") {
  950. if (snippet !== "^") {
  951. multi_mode = true;
  952. }
  953. }
  954. next_char();
  955. return true;
  956. }
  957. function sequence(follow) {
  958. if (factor()) {
  959. quantifier();
  960. return sequence(true);
  961. }
  962. if (!follow) {
  963. warn_at("expected_regexp_factor_a", line, column, char);
  964. }
  965. }
  966. // Match a choice (a sequence that can be followed by | and another choice).
  967. sequence();
  968. if (char === "|") {
  969. next_char("|");
  970. return choice();
  971. }
  972. }
  973. // Scan the regexp literal. Give a warning if the first character is = because
  974. // /= looks like a division assignment operator.
  975. snippet = "";
  976. next_char();
  977. if (char === "=") {
  978. warn_at("expected_a_before_b", line, column, "\\", "=");
  979. }
  980. choice();
  981. // Make sure there is a closing slash.
  982. snip();
  983. value = snippet;
  984. next_char("/");
  985. // Process dangling flag letters.
  986. const allowed = {
  987. g: true,
  988. i: true,
  989. m: true,
  990. u: true,
  991. y: true
  992. };
  993. const flag = empty();
  994. (function make_flag() {
  995. if (is_letter(char)) {
  996. if (allowed[char] !== true) {
  997. warn_at("unexpected_a", line, column, char);
  998. }
  999. allowed[char] = false;
  1000. flag[char] = true;
  1001. next_char();
  1002. return make_flag();
  1003. }
  1004. }());
  1005. back_char();
  1006. if (char === "/" || char === "*") {
  1007. return stop_at("unexpected_a", line, from, char);
  1008. }
  1009. result = make("(regexp)", char);
  1010. result.flag = flag;
  1011. result.value = value;
  1012. if (multi_mode && !flag.m) {
  1013. warn_at("missing_m", line, column);
  1014. }
  1015. return result;
  1016. }
  1017. function string(quote) {
  1018. // Make a string token.
  1019. let the_token;
  1020. snippet = "";
  1021. next_char();
  1022. return (function next() {
  1023. if (char === quote) {
  1024. snip();
  1025. the_token = make("(string)", snippet);
  1026. the_token.quote = quote;
  1027. return the_token;
  1028. }
  1029. if (char === "") {
  1030. return stop_at("unclosed_string", line, column);
  1031. }
  1032. if (char === "\\") {
  1033. escape(quote);
  1034. } else if (char === "`") {
  1035. if (mega_mode) {
  1036. warn_at("unexpected_a", line, column, "`");
  1037. }
  1038. next_char("`");
  1039. } else {
  1040. next_char();
  1041. }
  1042. return next();
  1043. }());
  1044. }
  1045. function frack() {
  1046. if (char === ".") {
  1047. some_digits(rx_digits);
  1048. next_char();
  1049. }
  1050. if (char === "E" || char === "e") {
  1051. next_char();
  1052. if (char !== "+" && char !== "-") {
  1053. back_char();
  1054. }
  1055. some_digits(rx_digits);
  1056. next_char();
  1057. }
  1058. }
  1059. function number() {
  1060. if (snippet === "0") {
  1061. next_char();
  1062. if (char === ".") {
  1063. frack();
  1064. } else if (char === "b") {
  1065. some_digits(rx_bits);
  1066. next_char();
  1067. } else if (char === "o") {
  1068. some_digits(rx_octals);
  1069. next_char();
  1070. } else if (char === "x") {
  1071. some_digits(rx_hexs);
  1072. next_char();
  1073. }
  1074. } else {
  1075. next_char();
  1076. frack();
  1077. }
  1078. // If the next character after a number is a digit or letter, then something
  1079. // unexpected is going on.
  1080. if (
  1081. (char >= "0" && char <= "9")
  1082. || (char >= "a" && char <= "z")
  1083. || (char >= "A" && char <= "Z")
  1084. ) {
  1085. return stop_at(
  1086. "unexpected_a_after_b",
  1087. line,
  1088. column - 1,
  1089. snippet.slice(-1),
  1090. snippet.slice(0, -1)
  1091. );
  1092. }
  1093. back_char();
  1094. return make("(number)", snippet);
  1095. }
  1096. function lex() {
  1097. let array;
  1098. let i = 0;
  1099. let j = 0;
  1100. let last;
  1101. let result;
  1102. let the_token;
  1103. // This should properly be a tail recursive function, but sadly, conformant
  1104. // implementations of ES6 are still rare. This is the ideal code:
  1105. // if (!source_line) {
  1106. // source_line = next_line();
  1107. // from = 0;
  1108. // return (
  1109. // source_line === undefined
  1110. // ? (
  1111. // mega_mode
  1112. // ? stop_at("unclosed_mega", mega_line, mega_from)
  1113. // : make("(end)")
  1114. // )
  1115. // : lex()
  1116. // );
  1117. // }
  1118. // Unfortunately, incompetent JavaScript engines will sometimes fail to execute
  1119. // it correctly. So for now, we do it the old fashioned way.
  1120. while (!source_line) {
  1121. source_line = next_line();
  1122. from = 0;
  1123. if (source_line === undefined) {
  1124. return (
  1125. mega_mode
  1126. ? stop_at("unclosed_mega", mega_line, mega_from)
  1127. : make("(end)")
  1128. );
  1129. }
  1130. }
  1131. from = column;
  1132. result = source_line.match(rx_token);
  1133. // result[1] token
  1134. // result[2] whitespace
  1135. // result[3] identifier
  1136. // result[4] number
  1137. // result[5] rest
  1138. if (!result) {
  1139. return stop_at(
  1140. "unexpected_char_a",
  1141. line,
  1142. column,
  1143. source_line[0]
  1144. );
  1145. }
  1146. snippet = result[1];
  1147. column += snippet.length;
  1148. source_line = result[5];
  1149. // Whitespace was matched. Call lex again to get more.
  1150. if (result[2]) {
  1151. return lex();
  1152. }
  1153. // The token is an identifier.
  1154. if (result[3]) {
  1155. return make(snippet, undefined, true);
  1156. }
  1157. // The token is a number.
  1158. if (result[4]) {
  1159. return number(snippet);
  1160. }
  1161. // The token is a string.
  1162. if (snippet === "\"") {
  1163. return string(snippet);
  1164. }
  1165. if (snippet === "'") {
  1166. if (!option.single) {
  1167. warn_at("use_double", line, column);
  1168. }
  1169. return string(snippet);
  1170. }
  1171. // The token is a megastring. We don't allow any kind of mega nesting.
  1172. if (snippet === "`") {
  1173. if (mega_mode) {
  1174. return stop_at("expected_a_b", line, column, "}", "`");
  1175. }
  1176. snippet = "";
  1177. mega_from = from;
  1178. mega_line = line;
  1179. mega_mode = true;
  1180. // Parsing a mega literal is tricky. First make a ` token.
  1181. make("`");
  1182. from += 1;
  1183. // Then loop, building up a string, possibly from many lines, until seeing
  1184. // the end of file, a closing `, or a ${ indicting an expression within the
  1185. // string.
  1186. (function part() {
  1187. const at = source_line.search(rx_mega);
  1188. // If neither ` nor ${ is seen, then the whole line joins the snippet.
  1189. if (at < 0) {
  1190. snippet += source_line + "\n";
  1191. return (
  1192. next_line() === undefined
  1193. ? stop_at("unclosed_mega", mega_line, mega_from)
  1194. : part()
  1195. );
  1196. }
  1197. // if either ` or ${ was found, then the preceding joins the snippet to become
  1198. // a string token.
  1199. snippet += source_line.slice(0, at);
  1200. column += at;
  1201. source_line = source_line.slice(at);
  1202. if (source_line[0] === "\\") {
  1203. stop_at("escape_mega", line, at);
  1204. }
  1205. make("(string)", snippet).quote = "`";
  1206. snippet = "";
  1207. // If ${, then make tokens that will become part of an expression until
  1208. // a } token is made.
  1209. if (source_line[0] === "$") {
  1210. column += 2;
  1211. make("${");
  1212. source_line = source_line.slice(2);
  1213. (function expr() {
  1214. const id = lex().id;
  1215. if (id === "{") {
  1216. return stop_at(
  1217. "expected_a_b",
  1218. line,
  1219. column,
  1220. "}",
  1221. "{"
  1222. );
  1223. }
  1224. if (id !== "}") {
  1225. return expr();
  1226. }
  1227. }());
  1228. return part();
  1229. }
  1230. }());
  1231. source_line = source_line.slice(1);
  1232. column += 1;
  1233. mega_mode = false;
  1234. return make("`");
  1235. }
  1236. // The token is a // comment.
  1237. if (snippet === "//") {
  1238. snippet = source_line;
  1239. source_line = "";
  1240. the_token = comment(snippet);
  1241. if (mega_mode) {
  1242. warn("unexpected_comment", the_token, "`");
  1243. }
  1244. return the_token;
  1245. }
  1246. // The token is a /* comment.
  1247. if (snippet === "/*") {
  1248. array = [];
  1249. if (source_line[0] === "/") {
  1250. warn_at("unexpected_a", line, column + i, "/");
  1251. }
  1252. (function next() {
  1253. if (source_line > "") {
  1254. i = source_line.search(rx_star_slash);
  1255. if (i >= 0) {
  1256. return;
  1257. }
  1258. j = source_line.search(rx_slash_star);
  1259. if (j >= 0) {
  1260. warn_at("nested_comment", line, column + j);
  1261. }
  1262. }
  1263. array.push(source_line);
  1264. source_line = next_line();
  1265. if (source_line === undefined) {
  1266. return stop_at("unclosed_comment", line, column);
  1267. }
  1268. return next();
  1269. }());
  1270. snippet = source_line.slice(0, i);
  1271. j = snippet.search(rx_slash_star_or_slash);
  1272. if (j >= 0) {
  1273. warn_at("nested_comment", line, column + j);
  1274. }
  1275. array.push(snippet);
  1276. column += i + 2;
  1277. source_line = source_line.slice(i + 2);
  1278. return comment(array);
  1279. }
  1280. // The token is a slash.
  1281. if (snippet === "/") {
  1282. // The / can be a division operator or the beginning of a regular expression
  1283. // literal. It is not possible to know which without doing a complete parse.
  1284. // We want to complete the tokenization before we begin to parse, so we will
  1285. // estimate. This estimator can fail in some cases. For example, it cannot
  1286. // know if "}" is ending a block or ending an object literal, so it can
  1287. // behave incorrectly in that case; it is not meaningful to divide an
  1288. // object, so it is likely that we can get away with it. We avoided the worst
  1289. // cases by eliminating automatic semicolon insertion.
  1290. if (prior.identifier) {
  1291. if (!prior.dot) {
  1292. if (prior.id === "return") {
  1293. return regexp();
  1294. }
  1295. if (
  1296. prior.id === "(begin)"
  1297. || prior.id === "case"
  1298. || prior.id === "delete"
  1299. || prior.id === "in"
  1300. || prior.id === "instanceof"
  1301. || prior.id === "new"
  1302. || prior.id === "typeof"
  1303. || prior.id === "void"
  1304. || prior.id === "yield"
  1305. ) {
  1306. the_token = regexp();
  1307. return stop("unexpected_a", the_token);
  1308. }
  1309. }
  1310. } else {
  1311. last = prior.id[prior.id.length - 1];
  1312. if ("(,=:?[".indexOf(last) >= 0) {
  1313. return regexp();
  1314. }
  1315. if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
  1316. the_token = regexp();
  1317. warn("wrap_regexp", the_token);
  1318. return the_token;
  1319. }
  1320. }
  1321. if (source_line[0] === "/") {
  1322. column += 1;
  1323. source_line = source_line.slice(1);
  1324. snippet = "/=";
  1325. warn_at("unexpected_a", line, column, "/=");
  1326. }
  1327. }
  1328. return make(snippet);
  1329. }
  1330. first = lex();
  1331. json_mode = first.id === "{" || first.id === "[";
  1332. // This loop will be replaced with a recursive call to lex when ES6 has been
  1333. // finished and widely deployed and adopted.
  1334. while (true) {
  1335. if (lex().id === "(end)") {
  1336. break;
  1337. }
  1338. }
  1339. }
  1340. // Parsing:
  1341. // Parsing weaves the tokens into an abstract syntax tree. During that process,
  1342. // a token may be given any of these properties:
  1343. // arity string
  1344. // label identifier
  1345. // name identifier
  1346. // expression expressions
  1347. // block statements
  1348. // else statements (else, default, catch)
  1349. // Specialized tokens may have additional properties.
  1350. function survey(name) {
  1351. let id = name.id;
  1352. // Tally the property name. If it is a string, only tally strings that conform
  1353. // to the identifier rules.
  1354. if (id === "(string)") {
  1355. id = name.value;
  1356. if (!rx_identifier.test(id)) {
  1357. return id;
  1358. }
  1359. } else if (id === "`") {
  1360. if (name.value.length === 1) {
  1361. id = name.value[0].value;
  1362. if (!rx_identifier.test(id)) {
  1363. return id;
  1364. }
  1365. }
  1366. } else if (!name.identifier) {
  1367. return stop("expected_identifier_a", name);
  1368. }
  1369. // If we have seen this name before, increment its count.
  1370. if (typeof property[id] === "number") {
  1371. property[id] += 1;
  1372. // If this is the first time seeing this property name, and if there is a
  1373. // tenure list, then it must be on the list. Otherwise, it must conform to
  1374. // the rules for good property names.
  1375. } else {
  1376. if (tenure !== undefined) {
  1377. if (tenure[id] !== true) {
  1378. warn("unregistered_property_a", name);
  1379. }
  1380. } else {
  1381. if (name.identifier && rx_bad_property.test(id)) {
  1382. warn("bad_property_a", name);
  1383. }
  1384. }
  1385. property[id] = 1;
  1386. }
  1387. return id;
  1388. }
  1389. function dispense() {
  1390. // Deliver the next token, skipping the comments.
  1391. const cadet = tokens[token_nr];
  1392. token_nr += 1;
  1393. if (cadet.id === "(comment)") {
  1394. if (json_mode) {
  1395. warn("unexpected_a", cadet);
  1396. }
  1397. return dispense();
  1398. } else {
  1399. return cadet;
  1400. }
  1401. }
  1402. function lookahead() {
  1403. // Look ahead one token without advancing.
  1404. const old_token_nr = token_nr;
  1405. const cadet = dispense(true);
  1406. token_nr = old_token_nr;
  1407. return cadet;
  1408. }
  1409. function advance(id, match) {
  1410. // Produce the next token.
  1411. // Attempt to give helpful names to anonymous functions.
  1412. if (token.identifier && token.id !== "function") {
  1413. anon = token.id;
  1414. } else if (token.id === "(string)" && rx_identifier.test(token.value)) {
  1415. anon = token.value;
  1416. }
  1417. // Attempt to match next_token with an expected id.
  1418. if (id !== undefined && next_token.id !== id) {
  1419. return (
  1420. match === undefined
  1421. ? stop("expected_a_b", next_token, id, artifact())
  1422. : stop(
  1423. "expected_a_b_from_c_d",
  1424. next_token,
  1425. id,
  1426. artifact(match),
  1427. artifact_line(match),
  1428. artifact(next_token)
  1429. )
  1430. );
  1431. }
  1432. // Promote the tokens, skipping comments.
  1433. token = next_token;
  1434. next_token = dispense();
  1435. if (next_token.id === "(end)") {
  1436. token_nr -= 1;
  1437. }
  1438. }
  1439. // Parsing of JSON is simple:
  1440. function json_value() {
  1441. let negative;
  1442. if (next_token.id === "{") {
  1443. return (function json_object() {
  1444. const brace = next_token;
  1445. const object = empty();
  1446. const properties = [];
  1447. brace.expression = properties;
  1448. advance("{");
  1449. if (next_token.id !== "}") {
  1450. (function next() {
  1451. let name;
  1452. let value;
  1453. if (next_token.quote !== "\"") {
  1454. warn(
  1455. "unexpected_a",
  1456. next_token,
  1457. next_token.quote
  1458. );
  1459. }
  1460. name = next_token;
  1461. advance("(string)");
  1462. if (object[token.value] !== undefined) {
  1463. warn("duplicate_a", token);
  1464. } else if (token.value === "__proto__") {
  1465. warn("bad_property_a", token);
  1466. } else {
  1467. object[token.value] = token;
  1468. }
  1469. advance(":");
  1470. value = json_value();
  1471. value.label = name;
  1472. properties.push(value);
  1473. if (next_token.id === ",") {
  1474. advance(",");
  1475. return next();
  1476. }
  1477. }());
  1478. }
  1479. advance("}", brace);
  1480. return brace;
  1481. }());
  1482. }
  1483. if (next_token.id === "[") {
  1484. return (function json_array() {
  1485. const bracket = next_token;
  1486. const elements = [];
  1487. bracket.expression = elements;
  1488. advance("[");
  1489. if (next_token.id !== "]") {
  1490. (function next() {
  1491. elements.push(json_value());
  1492. if (next_token.id === ",") {
  1493. advance(",");
  1494. return next();
  1495. }
  1496. }());
  1497. }
  1498. advance("]", bracket);
  1499. return bracket;
  1500. }());
  1501. }
  1502. if (
  1503. next_token.id === "true"
  1504. || next_token.id === "false"
  1505. || next_token.id === "null"
  1506. ) {
  1507. advance();
  1508. return token;
  1509. }
  1510. if (next_token.id === "(number)") {
  1511. if (!rx_JSON_number.test(next_token.value)) {
  1512. warn("unexpected_a");
  1513. }
  1514. advance();
  1515. return token;
  1516. }
  1517. if (next_token.id === "(string)") {
  1518. if (next_token.quote !== "\"") {
  1519. warn("unexpected_a", next_token, next_token.quote);
  1520. }
  1521. advance();
  1522. return token;
  1523. }
  1524. if (next_token.id === "-") {
  1525. negative = next_token;
  1526. negative.arity = "unary";
  1527. advance("-");
  1528. advance("(number)");
  1529. negative.expression = token;
  1530. return negative;
  1531. }
  1532. stop("unexpected_a");
  1533. }
  1534. // Now we parse JavaScript.
  1535. function enroll(name, role, readonly) {
  1536. // Enroll a name into the current function context. The role can be exception,
  1537. // function, label, parameter, or variable. We look for variable redefinition
  1538. // because it causes confusion.
  1539. const id = name.id;
  1540. // Reserved words may not be enrolled.
  1541. if (syntax[id] !== undefined && id !== "ignore") {
  1542. warn("reserved_a", name);
  1543. } else {
  1544. // Has the name been enrolled in this context?
  1545. let earlier = functionage.context[id];
  1546. if (earlier) {
  1547. warn(
  1548. "redefinition_a_b",
  1549. name,
  1550. name.id,
  1551. earlier.line + fudge
  1552. );
  1553. // Has the name been enrolled in an outer context?
  1554. } else {
  1555. stack.forEach(function (value) {
  1556. const item = value.context[id];
  1557. if (item !== undefined) {
  1558. earlier = item;
  1559. }
  1560. });
  1561. if (earlier) {
  1562. if (id === "ignore") {
  1563. if (earlier.role === "variable") {
  1564. warn("unexpected_a", name);
  1565. }
  1566. } else {
  1567. if (
  1568. (
  1569. role !== "exception"
  1570. || earlier.role !== "exception"
  1571. )
  1572. && role !== "parameter"
  1573. && role !== "function"
  1574. ) {
  1575. warn(
  1576. "redefinition_a_b",
  1577. name,
  1578. name.id,
  1579. earlier.line + fudge
  1580. );
  1581. }
  1582. }
  1583. }
  1584. // Enroll it.
  1585. functionage.context[id] = name;
  1586. name.dead = true;
  1587. name.parent = functionage;
  1588. name.init = false;
  1589. name.role = role;
  1590. name.used = 0;
  1591. name.writable = !readonly;
  1592. }
  1593. }
  1594. }
  1595. function expression(rbp, initial) {
  1596. // This is the heart of the Pratt parser. I retained Pratt's nomenclature.
  1597. // They are elements of the parsing method called Top Down Operator Precedence.
  1598. // nud Null denotation
  1599. // led Left denotation
  1600. // lbp Left binding power
  1601. // rbp Right binding power
  1602. // It processes a nud (variable, constant, prefix operator). It will then
  1603. // process leds (infix operators) until the bind powers cause it to stop. It
  1604. // returns the expression's parse tree.
  1605. let left;
  1606. let the_symbol;
  1607. // Statements will have already advanced, so advance now only if the token is
  1608. // not the first of a statement,
  1609. if (!initial) {
  1610. advance();
  1611. }
  1612. the_symbol = syntax[token.id];
  1613. if (the_symbol !== undefined && the_symbol.nud !== undefined) {
  1614. left = the_symbol.nud();
  1615. } else if (token.identifier) {
  1616. left = token;
  1617. left.arity = "variable";
  1618. } else {
  1619. return stop("unexpected_a", token);
  1620. }
  1621. (function right() {
  1622. the_symbol = syntax[next_token.id];
  1623. if (
  1624. the_symbol !== undefined
  1625. && the_symbol.led !== undefined
  1626. && rbp < the_symbol.lbp
  1627. ) {
  1628. advance();
  1629. left = the_symbol.led(left);
  1630. return right();
  1631. }
  1632. }());
  1633. return left;
  1634. }
  1635. function condition() {
  1636. // Parse the condition part of a do, if, while.
  1637. const the_paren = next_token;
  1638. let the_value;
  1639. the_paren.free = true;
  1640. advance("(");
  1641. the_value = expression(0);
  1642. advance(")");
  1643. if (the_value.wrapped === true) {
  1644. warn("unexpected_a", the_paren);
  1645. }
  1646. if (anticondition[the_value.id] === true) {
  1647. warn("unexpected_a", the_value);
  1648. }
  1649. return the_value;
  1650. }
  1651. function is_weird(thing) {
  1652. return (
  1653. thing.id === "(regexp)"
  1654. || thing.id === "{"
  1655. || thing.id === "=>"
  1656. || thing.id === "function"
  1657. || (thing.id === "[" && thing.arity === "unary")
  1658. );
  1659. }
  1660. function are_similar(a, b) {
  1661. if (a === b) {
  1662. return true;
  1663. }
  1664. if (Array.isArray(a)) {
  1665. return (
  1666. Array.isArray(b)
  1667. && a.length === b.length
  1668. && a.every(function (value, index) {
  1669. return are_similar(value, b[index]);
  1670. })
  1671. );
  1672. }
  1673. if (Array.isArray(b)) {
  1674. return false;
  1675. }
  1676. if (a.id === "(number)" && b.id === "(number)") {
  1677. return a.value === b.value;
  1678. }
  1679. let a_string;
  1680. let b_string;
  1681. if (a.id === "(string)") {
  1682. a_string = a.value;
  1683. } else if (a.id === "`" && a.constant) {
  1684. a_string = a.value[0];
  1685. }
  1686. if (b.id === "(string)") {
  1687. b_string = b.value;
  1688. } else if (b.id === "`" && b.constant) {
  1689. b_string = b.value[0];
  1690. }
  1691. if (typeof a_string === "string") {
  1692. return a_string === b_string;
  1693. }
  1694. if (is_weird(a) || is_weird(b)) {
  1695. return false;
  1696. }
  1697. if (a.arity === b.arity && a.id === b.id) {
  1698. if (a.id === ".") {
  1699. return (
  1700. are_similar(a.expression, b.expression)
  1701. && are_similar(a.name, b.name)
  1702. );
  1703. }
  1704. if (a.arity === "unary") {
  1705. return are_similar(a.expression, b.expression);
  1706. }
  1707. if (a.arity === "binary") {
  1708. return (
  1709. a.id !== "("
  1710. && are_similar(a.expression[0], b.expression[0])
  1711. && are_similar(a.expression[1], b.expression[1])
  1712. );
  1713. }
  1714. if (a.arity === "ternary") {
  1715. return (
  1716. are_similar(a.expression[0], b.expression[0])
  1717. && are_similar(a.expression[1], b.expression[1])
  1718. && are_similar(a.expression[2], b.expression[2])
  1719. );
  1720. }
  1721. if (a.arity === "function" && a.arity === "regexp") {
  1722. return false;
  1723. }
  1724. return true;
  1725. }
  1726. return false;
  1727. }
  1728. function semicolon() {
  1729. // Try to match a semicolon.
  1730. if (next_token.id === ";") {
  1731. advance(";");
  1732. } else {
  1733. warn_at(
  1734. "expected_a_b",
  1735. token.line,
  1736. token.thru,
  1737. ";",
  1738. artifact(next_token)
  1739. );
  1740. }
  1741. anon = "anonymous";
  1742. }
  1743. function statement() {
  1744. // Parse a statement. Any statement may have a label, but only four statements
  1745. // have use for one. A statement can be one of the standard statements, or
  1746. // an assignment expression, or an invocation expression.
  1747. let first;
  1748. let the_label;
  1749. let the_statement;
  1750. let the_symbol;
  1751. advance();
  1752. if (token.identifier && next_token.id === ":") {
  1753. the_label = token;
  1754. if (the_label.id === "ignore") {
  1755. warn("unexpected_a", the_label);
  1756. }
  1757. advance(":");
  1758. if (
  1759. next_token.id === "do"
  1760. || next_token.id === "for"
  1761. || next_token.id === "switch"
  1762. || next_token.id === "while"
  1763. ) {
  1764. enroll(the_label, "label", true);
  1765. the_label.init = true;
  1766. the_label.dead = false;
  1767. the_statement = statement();
  1768. the_statement.label = the_label;
  1769. the_statement.statement = true;
  1770. return the_statement;
  1771. }
  1772. advance();
  1773. warn("unexpected_label_a", the_label);
  1774. }
  1775. // Parse the statement.
  1776. first = token;
  1777. first.statement = true;
  1778. the_symbol = syntax[first.id];
  1779. if (the_symbol !== undefined && the_symbol.fud !== undefined) {
  1780. the_symbol.disrupt = false;
  1781. the_symbol.statement = true;
  1782. the_statement = the_symbol.fud();
  1783. } else {
  1784. // It is an expression statement.
  1785. the_statement = expression(0, true);
  1786. if (the_statement.wrapped && the_statement.id !== "(") {
  1787. warn("unexpected_a", first);
  1788. }
  1789. semicolon();
  1790. }
  1791. if (the_label !== undefined) {
  1792. the_label.dead = true;
  1793. }
  1794. return the_statement;
  1795. }
  1796. function statements() {
  1797. // Parse a list of statements. Give a warning if an unreachable statement
  1798. // follows a disruptive statement.
  1799. const array = [];
  1800. (function next(disrupt) {
  1801. if (
  1802. next_token.id !== "}"
  1803. && next_token.id !== "case"
  1804. && next_token.id !== "default"
  1805. && next_token.id !== "else"
  1806. && next_token.id !== "(end)"
  1807. ) {
  1808. let a_statement = statement();
  1809. array.push(a_statement);
  1810. if (disrupt) {
  1811. warn("unreachable_a", a_statement);
  1812. }
  1813. return next(a_statement.disrupt);
  1814. }
  1815. }(false));
  1816. return array;
  1817. }
  1818. function not_top_level(thing) {
  1819. // Some features should not be at the outermost level.
  1820. if (functionage === global) {
  1821. warn("unexpected_at_top_level_a", thing);
  1822. }
  1823. }
  1824. function top_level_only(the_thing) {
  1825. // Some features must be at the most outermost level.
  1826. if (blockage !== global) {
  1827. warn("misplaced_a", the_thing);
  1828. }
  1829. }
  1830. function block(special) {
  1831. // Parse a block, a sequence of statements wrapped in braces.
  1832. // special "body" The block is a function body.
  1833. // "ignore" No warning on an empty block.
  1834. // "naked" No advance.
  1835. // undefined An ordinary block.
  1836. let stmts;
  1837. let the_block;
  1838. if (special !== "naked") {
  1839. advance("{");
  1840. }
  1841. the_block = token;
  1842. the_block.arity = "statement";
  1843. the_block.body = special === "body";
  1844. // Top level function bodies may include the "use strict" pragma.
  1845. if (
  1846. special === "body"
  1847. && stack.length === 1
  1848. && next_token.value === "use strict"
  1849. ) {
  1850. next_token.statement = true;
  1851. advance("(string)");
  1852. advance(";");
  1853. }
  1854. stmts = statements();
  1855. the_block.block = stmts;
  1856. if (stmts.length === 0) {
  1857. if (!option.devel && special !== "ignore") {
  1858. warn("empty_block", the_block);
  1859. }
  1860. the_block.disrupt = false;
  1861. } else {
  1862. the_block.disrupt = stmts[stmts.length - 1].disrupt;
  1863. }
  1864. advance("}");
  1865. return the_block;
  1866. }
  1867. function mutation_check(the_thing) {
  1868. // The only expressions that may be assigned to are
  1869. // e.b
  1870. // e[b]
  1871. // v
  1872. // [destructure]
  1873. // {destructure}
  1874. if (
  1875. the_thing.arity !== "variable"
  1876. && the_thing.id !== "."
  1877. && the_thing.id !== "["
  1878. && the_thing.id !== "{"
  1879. ) {
  1880. warn("bad_assignment_a", the_thing);
  1881. return false;
  1882. }
  1883. return true;
  1884. }
  1885. function left_check(left, right) {
  1886. // Warn if the left is not one of these:
  1887. // e.b
  1888. // e[b]
  1889. // e()
  1890. // ?:
  1891. // identifier
  1892. const id = left.id;
  1893. if (
  1894. !left.identifier
  1895. && (
  1896. left.arity !== "ternary"
  1897. || (
  1898. !left_check(left.expression[1])
  1899. && !left_check(left.expression[2])
  1900. )
  1901. )
  1902. && (
  1903. left.arity !== "binary"
  1904. || (id !== "." && id !== "(" && id !== "[")
  1905. )
  1906. ) {
  1907. warn("unexpected_a", right);
  1908. return false;
  1909. }
  1910. return true;
  1911. }
  1912. // These functions are used to specify the grammar of our language:
  1913. function symbol(id, bp) {
  1914. // Make a symbol if it does not already exist in the language's syntax.
  1915. let the_symbol = syntax[id];
  1916. if (the_symbol === undefined) {
  1917. the_symbol = empty();
  1918. the_symbol.id = id;
  1919. the_symbol.lbp = bp || 0;
  1920. syntax[id] = the_symbol;
  1921. }
  1922. return the_symbol;
  1923. }
  1924. function assignment(id) {
  1925. // Make an assignment operator. The one true assignment is different because
  1926. // its left side, when it is a variable, is not treated as an expression.
  1927. // That case is special because that is when a variable gets initialized. The
  1928. // other assignment operators can modify, but they cannot initialize.
  1929. const the_symbol = symbol(id, 20);
  1930. the_symbol.led = function (left) {
  1931. const the_token = token;
  1932. let right;
  1933. the_token.arity = "assignment";
  1934. right = expression(20 - 1);
  1935. if (id === "=" && left.arity === "variable") {
  1936. the_token.names = left;
  1937. the_token.expression = right;
  1938. } else {
  1939. the_token.expression = [left, right];
  1940. }
  1941. if (
  1942. right.arity === "assignment"
  1943. || right.arity === "pre"
  1944. || right.arity === "post"
  1945. ) {
  1946. warn("unexpected_a", right);
  1947. }
  1948. mutation_check(left);
  1949. return the_token;
  1950. };
  1951. return the_symbol;
  1952. }
  1953. function constant(id, type, value) {
  1954. // Make a constant symbol.
  1955. const the_symbol = symbol(id);
  1956. the_symbol.constant = true;
  1957. the_symbol.nud = (
  1958. typeof value === "function"
  1959. ? value
  1960. : function () {
  1961. token.constant = true;
  1962. if (value !== undefined) {
  1963. token.value = value;
  1964. }
  1965. return token;
  1966. }
  1967. );
  1968. the_symbol.type = type;
  1969. the_symbol.value = value;
  1970. return the_symbol;
  1971. }
  1972. function infix(id, bp, f) {
  1973. // Make an infix operator.
  1974. const the_symbol = symbol(id, bp);
  1975. the_symbol.led = function (left) {
  1976. const the_token = token;
  1977. the_token.arity = "binary";
  1978. if (f !== undefined) {
  1979. return f(left);
  1980. }
  1981. the_token.expression = [left, expression(bp)];
  1982. return the_token;
  1983. };
  1984. return the_symbol;
  1985. }
  1986. function infixr(id, bp) {
  1987. // Make a right associative infix operator.
  1988. const the_symbol = symbol(id, bp);
  1989. the_symbol.led = function (left) {
  1990. const the_token = token;
  1991. the_token.arity = "binary";
  1992. the_token.expression = [left, expression(bp - 1)];
  1993. return the_token;
  1994. };
  1995. return the_symbol;
  1996. }
  1997. function post(id) {
  1998. // Make one of the post operators.
  1999. const the_symbol = symbol(id, 150);
  2000. the_symbol.led = function (left) {
  2001. token.expression = left;
  2002. token.arity = "post";
  2003. mutation_check(token.expression);
  2004. return token;
  2005. };
  2006. return the_symbol;
  2007. }
  2008. function pre(id) {
  2009. // Make one of the pre operators.
  2010. const the_symbol = symbol(id);
  2011. the_symbol.nud = function () {
  2012. const the_token = token;
  2013. the_token.arity = "pre";
  2014. the_token.expression = expression(150);
  2015. mutation_check(the_token.expression);
  2016. return the_token;
  2017. };
  2018. return the_symbol;
  2019. }
  2020. function prefix(id, f) {
  2021. // Make a prefix operator.
  2022. const the_symbol = symbol(id);
  2023. the_symbol.nud = function () {
  2024. const the_token = token;
  2025. the_token.arity = "unary";
  2026. if (typeof f === "function") {
  2027. return f();
  2028. }
  2029. the_token.expression = expression(150);
  2030. return the_token;
  2031. };
  2032. return the_symbol;
  2033. }
  2034. function stmt(id, f) {
  2035. // Make a statement.
  2036. const the_symbol = symbol(id);
  2037. the_symbol.fud = function () {
  2038. token.arity = "statement";
  2039. return f();
  2040. };
  2041. return the_symbol;
  2042. }
  2043. function ternary(id1, id2) {
  2044. // Make a ternary operator.
  2045. const the_symbol = symbol(id1, 30);
  2046. the_symbol.led = function (left) {
  2047. const the_token = token;
  2048. const second = expression(20);
  2049. advance(id2);
  2050. token.arity = "ternary";
  2051. the_token.arity = "ternary";
  2052. the_token.expression = [left, second, expression(10)];
  2053. if (next_token.id !== ")") {
  2054. warn("use_open", the_token);
  2055. }
  2056. return the_token;
  2057. };
  2058. return the_symbol;
  2059. }
  2060. // Begin defining the language.
  2061. syntax = empty();
  2062. symbol("}");
  2063. symbol(")");
  2064. symbol("]");
  2065. symbol(",");
  2066. symbol(";");
  2067. symbol(":");
  2068. symbol("*/");
  2069. symbol("await");
  2070. symbol("case");
  2071. symbol("catch");
  2072. symbol("class");
  2073. symbol("default");
  2074. symbol("else");
  2075. symbol("enum");
  2076. symbol("finally");
  2077. symbol("implements");
  2078. symbol("interface");
  2079. symbol("package");
  2080. symbol("private");
  2081. symbol("protected");
  2082. symbol("public");
  2083. symbol("static");
  2084. symbol("super");
  2085. symbol("void");
  2086. symbol("yield");
  2087. constant("(number)", "number");
  2088. constant("(regexp)", "regexp");
  2089. constant("(string)", "string");
  2090. constant("arguments", "object", function () {
  2091. warn("unexpected_a", token);
  2092. return token;
  2093. });
  2094. constant("eval", "function", function () {
  2095. if (!option.eval) {
  2096. warn("unexpected_a", token);
  2097. } else if (next_token.id !== "(") {
  2098. warn("expected_a_before_b", next_token, "(", artifact());
  2099. }
  2100. return token;
  2101. });
  2102. constant("false", "boolean", false);
  2103. constant("Function", "function", function () {
  2104. if (!option.eval) {
  2105. warn("unexpected_a", token);
  2106. } else if (next_token.id !== "(") {
  2107. warn("expected_a_before_b", next_token, "(", artifact());
  2108. }
  2109. return token;
  2110. });
  2111. constant("ignore", "undefined", function () {
  2112. warn("unexpected_a", token);
  2113. return token;
  2114. });
  2115. constant("Infinity", "number", Infinity);
  2116. constant("isFinite", "function", function () {
  2117. warn("expected_a_b", token, "Number.isFinite", "isFinite");
  2118. return token;
  2119. });
  2120. constant("isNaN", "function", function () {
  2121. warn("number_isNaN", token);
  2122. return token;
  2123. });
  2124. constant("NaN", "number", NaN);
  2125. constant("null", "null", null);
  2126. constant("this", "object", function () {
  2127. if (!option.this) {
  2128. warn("unexpected_a", token);
  2129. }
  2130. return token;
  2131. });
  2132. constant("true", "boolean", true);
  2133. constant("undefined", "undefined");
  2134. assignment("=");
  2135. assignment("+=");
  2136. assignment("-=");
  2137. assignment("*=");
  2138. assignment("/=");
  2139. assignment("%=");
  2140. assignment("&=");
  2141. assignment("|=");
  2142. assignment("^=");
  2143. assignment("<<=");
  2144. assignment(">>=");
  2145. assignment(">>>=");
  2146. infix("||", 40);
  2147. infix("&&", 50);
  2148. infix("|", 70);
  2149. infix("^", 80);
  2150. infix("&", 90);
  2151. infix("==", 100);
  2152. infix("===", 100);
  2153. infix("!=", 100);
  2154. infix("!==", 100);
  2155. infix("<", 110);
  2156. infix(">", 110);
  2157. infix("<=", 110);
  2158. infix(">=", 110);
  2159. infix("in", 110);
  2160. infix("instanceof", 110);
  2161. infix("<<", 120);
  2162. infix(">>", 120);
  2163. infix(">>>", 120);
  2164. infix("+", 130);
  2165. infix("-", 130);
  2166. infix("*", 140);
  2167. infix("/", 140);
  2168. infix("%", 140);
  2169. infixr("**", 150);
  2170. infix("(", 160, function (left) {
  2171. const the_paren = token;
  2172. let the_argument;
  2173. if (left.id !== "function") {
  2174. left_check(left, the_paren);
  2175. }
  2176. if (functionage.arity === "statement" && left.identifier) {
  2177. functionage.name.calls[left.id] = left;
  2178. }
  2179. the_paren.expression = [left];
  2180. if (next_token.id !== ")") {
  2181. (function next() {
  2182. let ellipsis;
  2183. if (next_token.id === "...") {
  2184. ellipsis = true;
  2185. advance("...");
  2186. }
  2187. the_argument = expression(10);
  2188. if (ellipsis) {
  2189. the_argument.ellipsis = true;
  2190. }
  2191. the_paren.expression.push(the_argument);
  2192. if (next_token.id === ",") {
  2193. advance(",");
  2194. return next();
  2195. }
  2196. }());
  2197. }
  2198. advance(")", the_paren);
  2199. if (the_paren.expression.length === 2) {
  2200. the_paren.free = true;
  2201. if (the_argument.wrapped === true) {
  2202. warn("unexpected_a", the_paren);
  2203. }
  2204. if (the_argument.id === "(") {
  2205. the_argument.wrapped = true;
  2206. }
  2207. } else {
  2208. the_paren.free = false;
  2209. }
  2210. return the_paren;
  2211. });
  2212. infix(".", 170, function (left) {
  2213. const the_token = token;
  2214. const name = next_token;
  2215. if (
  2216. (
  2217. left.id !== "(string)"
  2218. || (name.id !== "indexOf" && name.id !== "repeat")
  2219. )
  2220. && (
  2221. left.id !== "["
  2222. || (
  2223. name.id !== "concat"
  2224. && name.id !== "forEach"
  2225. && name.id !== "join"
  2226. && name.id !== "map"
  2227. )
  2228. )
  2229. && (left.id !== "+" || name.id !== "slice")
  2230. && (
  2231. left.id !== "(regexp)"
  2232. || (name.id !== "exec" && name.id !== "test")
  2233. )
  2234. ) {
  2235. left_check(left, the_token);
  2236. }
  2237. if (!name.identifier) {
  2238. stop("expected_identifier_a");
  2239. }
  2240. advance();
  2241. survey(name);
  2242. // The property name is not an expression.
  2243. the_token.name = name;
  2244. the_token.expression = left;
  2245. return the_token;
  2246. });
  2247. infix("?.", 170, function (left) {
  2248. const the_token = token;
  2249. const name = next_token;
  2250. if (
  2251. (
  2252. left.id !== "(string)"
  2253. || (name.id !== "indexOf" && name.id !== "repeat")
  2254. )
  2255. && (
  2256. left.id !== "["
  2257. || (
  2258. name.id !== "concat"
  2259. && name.id !== "forEach"
  2260. && name.id !== "join"
  2261. && name.id !== "map"
  2262. )
  2263. )
  2264. && (left.id !== "+" || name.id !== "slice")
  2265. && (
  2266. left.id !== "(regexp)"
  2267. || (name.id !== "exec" && name.id !== "test")
  2268. )
  2269. ) {
  2270. left_check(left, the_token);
  2271. }
  2272. if (!name.identifier) {
  2273. stop("expected_identifier_a");
  2274. }
  2275. advance();
  2276. survey(name);
  2277. // The property name is not an expression.
  2278. the_token.name = name;
  2279. the_token.expression = left;
  2280. return the_token;
  2281. });
  2282. infix("[", 170, function (left) {
  2283. const the_token = token;
  2284. const the_subscript = expression(0);
  2285. if (the_subscript.id === "(string)" || the_subscript.id === "`") {
  2286. const name = survey(the_subscript);
  2287. if (rx_identifier.test(name)) {
  2288. warn("subscript_a", the_subscript, name);
  2289. }
  2290. }
  2291. left_check(left, the_token);
  2292. the_token.expression = [left, the_subscript];
  2293. advance("]");
  2294. return the_token;
  2295. });
  2296. infix("=>", 170, function (left) {
  2297. return stop("wrap_parameter", left);
  2298. });
  2299. function do_tick() {
  2300. const the_tick = token;
  2301. the_tick.value = [];
  2302. the_tick.expression = [];
  2303. if (next_token.id !== "`") {
  2304. (function part() {
  2305. advance("(string)");
  2306. the_tick.value.push(token);
  2307. if (next_token.id === "${") {
  2308. advance("${");
  2309. the_tick.expression.push(expression(0));
  2310. advance("}");
  2311. return part();
  2312. }
  2313. }());
  2314. }
  2315. advance("`");
  2316. return the_tick;
  2317. }
  2318. infix("`", 160, function (left) {
  2319. const the_tick = do_tick();
  2320. left_check(left, the_tick);
  2321. the_tick.expression = [left].concat(the_tick.expression);
  2322. return the_tick;
  2323. });
  2324. post("++");
  2325. post("--");
  2326. pre("++");
  2327. pre("--");
  2328. prefix("+");
  2329. prefix("-");
  2330. prefix("~");
  2331. prefix("!");
  2332. prefix("!!");
  2333. prefix("[", function () {
  2334. const the_token = token;
  2335. the_token.expression = [];
  2336. if (next_token.id !== "]") {
  2337. (function next() {
  2338. let element;
  2339. let ellipsis = false;
  2340. if (next_token.id === "...") {
  2341. ellipsis = true;
  2342. advance("...");
  2343. }
  2344. element = expression(10);
  2345. if (ellipsis) {
  2346. element.ellipsis = true;
  2347. }
  2348. the_token.expression.push(element);
  2349. if (next_token.id === ",") {
  2350. advance(",");
  2351. return next();
  2352. }
  2353. }());
  2354. }
  2355. advance("]");
  2356. return the_token;
  2357. });
  2358. prefix("/=", function () {
  2359. stop("expected_a_b", token, "/\\=", "/=");
  2360. });
  2361. prefix("=>", function () {
  2362. return stop("expected_a_before_b", token, "()", "=>");
  2363. });
  2364. prefix("new", function () {
  2365. const the_new = token;
  2366. const right = expression(160);
  2367. if (next_token.id !== "(") {
  2368. warn("expected_a_before_b", next_token, "()", artifact(next_token));
  2369. }
  2370. the_new.expression = right;
  2371. return the_new;
  2372. });
  2373. prefix("typeof");
  2374. prefix("void", function () {
  2375. const the_void = token;
  2376. warn("unexpected_a", the_void);
  2377. the_void.expression = expression(0);
  2378. return the_void;
  2379. });
  2380. function parameter_list() {
  2381. const list = [];
  2382. let optional;
  2383. const signature = ["("];
  2384. if (next_token.id !== ")" && next_token.id !== "(end)") {
  2385. (function parameter() {
  2386. let ellipsis = false;
  2387. let param;
  2388. if (next_token.id === "{") {
  2389. if (optional !== undefined) {
  2390. warn(
  2391. "required_a_optional_b",
  2392. next_token,
  2393. next_token.id,
  2394. optional.id
  2395. );
  2396. }
  2397. param = next_token;
  2398. param.names = [];
  2399. advance("{");
  2400. signature.push("{");
  2401. (function subparameter() {
  2402. let subparam = next_token;
  2403. if (!subparam.identifier) {
  2404. return stop("expected_identifier_a");
  2405. }
  2406. survey(subparam);
  2407. advance();
  2408. signature.push(subparam.id);
  2409. if (next_token.id === ":") {
  2410. advance(":");
  2411. advance();
  2412. token.label = subparam;
  2413. subparam = token;
  2414. if (!subparam.identifier) {
  2415. return stop("expected_identifier_a");
  2416. }
  2417. }
  2418. if (next_token.id === "=") {
  2419. advance("=");
  2420. subparam.expression = expression();
  2421. param.open = true;
  2422. }
  2423. param.names.push(subparam);
  2424. if (next_token.id === ",") {
  2425. advance(",");
  2426. signature.push(", ");
  2427. return subparameter();
  2428. }
  2429. }());
  2430. list.push(param);
  2431. advance("}");
  2432. signature.push("}");
  2433. if (next_token.id === ",") {
  2434. advance(",");
  2435. signature.push(", ");
  2436. return parameter();
  2437. }
  2438. } else if (next_token.id === "[") {
  2439. if (optional !== undefined) {
  2440. warn(
  2441. "required_a_optional_b",
  2442. next_token,
  2443. next_token.id,
  2444. optional.id
  2445. );
  2446. }
  2447. param = next_token;
  2448. param.names = [];
  2449. advance("[");
  2450. signature.push("[]");
  2451. (function subparameter() {
  2452. const subparam = next_token;
  2453. if (!subparam.identifier) {
  2454. return stop("expected_identifier_a");
  2455. }
  2456. advance();
  2457. param.names.push(subparam);
  2458. if (next_token.id === "=") {
  2459. advance("=");
  2460. subparam.expression = expression();
  2461. param.open = true;
  2462. }
  2463. if (next_token.id === ",") {
  2464. advance(",");
  2465. return subparameter();
  2466. }
  2467. }());
  2468. list.push(param);
  2469. advance("]");
  2470. if (next_token.id === ",") {
  2471. advance(",");
  2472. signature.push(", ");
  2473. return parameter();
  2474. }
  2475. } else {
  2476. if (next_token.id === "...") {
  2477. ellipsis = true;
  2478. signature.push("...");
  2479. advance("...");
  2480. if (optional !== undefined) {
  2481. warn(
  2482. "required_a_optional_b",
  2483. next_token,
  2484. next_token.id,
  2485. optional.id
  2486. );
  2487. }
  2488. }
  2489. if (!next_token.identifier) {
  2490. return stop("expected_identifier_a");
  2491. }
  2492. param = next_token;
  2493. list.push(param);
  2494. advance();
  2495. signature.push(param.id);
  2496. if (ellipsis) {
  2497. param.ellipsis = true;
  2498. } else {
  2499. if (next_token.id === "=") {
  2500. optional = param;
  2501. advance("=");
  2502. param.expression = expression(0);
  2503. } else {
  2504. if (optional !== undefined) {
  2505. warn(
  2506. "required_a_optional_b",
  2507. param,
  2508. param.id,
  2509. optional.id
  2510. );
  2511. }
  2512. }
  2513. if (next_token.id === ",") {
  2514. advance(",");
  2515. signature.push(", ");
  2516. return parameter();
  2517. }
  2518. }
  2519. }
  2520. }());
  2521. }
  2522. advance(")");
  2523. signature.push(")");
  2524. return [list, signature.join("")];
  2525. }
  2526. function do_function(the_function) {
  2527. let name;
  2528. if (the_function === undefined) {
  2529. the_function = token;
  2530. // A function statement must have a name that will be in the parent's scope.
  2531. if (the_function.arity === "statement") {
  2532. if (!next_token.identifier) {
  2533. return stop("expected_identifier_a", next_token);
  2534. }
  2535. name = next_token;
  2536. enroll(name, "variable", true);
  2537. the_function.name = name;
  2538. name.init = true;
  2539. name.calls = empty();
  2540. advance();
  2541. } else if (name === undefined) {
  2542. // A function expression may have an optional name.
  2543. if (next_token.identifier) {
  2544. name = next_token;
  2545. the_function.name = name;
  2546. advance();
  2547. } else {
  2548. the_function.name = anon;
  2549. }
  2550. }
  2551. } else {
  2552. name = the_function.name;
  2553. }
  2554. the_function.level = functionage.level + 1;
  2555. if (mega_mode) {
  2556. warn("unexpected_a", the_function);
  2557. }
  2558. // Don't make functions in loops. It is inefficient, and it can lead to scoping
  2559. // errors.
  2560. if (functionage.loop > 0) {
  2561. warn("function_in_loop", the_function);
  2562. }
  2563. // Give the function properties for storing its names and for observing the
  2564. // depth of loops and switches.
  2565. the_function.context = empty();
  2566. the_function.finally = 0;
  2567. the_function.loop = 0;
  2568. the_function.switch = 0;
  2569. the_function.try = 0;
  2570. // Push the current function context and establish a new one.
  2571. stack.push(functionage);
  2572. functions.push(the_function);
  2573. functionage = the_function;
  2574. if (the_function.arity !== "statement" && typeof name === "object") {
  2575. enroll(name, "function", true);
  2576. name.dead = false;
  2577. name.init = true;
  2578. name.used = 1;
  2579. }
  2580. // Parse the parameter list.
  2581. advance("(");
  2582. token.free = false;
  2583. token.arity = "function";
  2584. [functionage.parameters, functionage.signature] = parameter_list();
  2585. functionage.parameters.forEach(function enroll_parameter(name) {
  2586. if (name.identifier) {
  2587. enroll(name, "parameter", false);
  2588. } else {
  2589. name.names.forEach(enroll_parameter);
  2590. }
  2591. });
  2592. // The function's body is a block.
  2593. the_function.block = block("body");
  2594. if (
  2595. the_function.arity === "statement"
  2596. && next_token.line === token.line
  2597. ) {
  2598. return stop("unexpected_a", next_token);
  2599. }
  2600. if (
  2601. next_token.id === "."
  2602. || next_token.id === "?."
  2603. || next_token.id === "["
  2604. ) {
  2605. warn("unexpected_a");
  2606. }
  2607. // Restore the previous context.
  2608. functionage = stack.pop();
  2609. return the_function;
  2610. }
  2611. prefix("function", do_function);
  2612. function fart(pl) {
  2613. advance("=>");
  2614. const the_fart = token;
  2615. the_fart.arity = "binary";
  2616. the_fart.name = "=>";
  2617. the_fart.level = functionage.level + 1;
  2618. functions.push(the_fart);
  2619. if (functionage.loop > 0) {
  2620. warn("function_in_loop", the_fart);
  2621. }
  2622. // Give the function properties storing its names and for observing the depth
  2623. // of loops and switches.
  2624. the_fart.context = empty();
  2625. the_fart.finally = 0;
  2626. the_fart.loop = 0;
  2627. the_fart.switch = 0;
  2628. the_fart.try = 0;
  2629. // Push the current function context and establish a new one.
  2630. stack.push(functionage);
  2631. functionage = the_fart;
  2632. the_fart.parameters = pl[0];
  2633. the_fart.signature = pl[1];
  2634. the_fart.parameters.forEach(function (name) {
  2635. enroll(name, "parameter", true);
  2636. });
  2637. if (next_token.id === "{") {
  2638. warn("expected_a_b", the_fart, "function", "=>");
  2639. the_fart.block = block("body");
  2640. } else {
  2641. the_fart.expression = expression(0);
  2642. }
  2643. functionage = stack.pop();
  2644. return the_fart;
  2645. }
  2646. prefix("(", function () {
  2647. const the_paren = token;
  2648. let the_value;
  2649. const cadet = lookahead().id;
  2650. // We can distinguish between a parameter list for => and a wrapped expression
  2651. // with one token of lookahead.
  2652. if (
  2653. next_token.id === ")"
  2654. || next_token.id === "..."
  2655. || (next_token.identifier && (cadet === "," || cadet === "="))
  2656. ) {
  2657. the_paren.free = false;
  2658. return fart(parameter_list());
  2659. }
  2660. the_paren.free = true;
  2661. the_value = expression(0);
  2662. if (the_value.wrapped === true) {
  2663. warn("unexpected_a", the_paren);
  2664. }
  2665. the_value.wrapped = true;
  2666. advance(")", the_paren);
  2667. if (next_token.id === "=>") {
  2668. if (the_value.arity !== "variable") {
  2669. if (the_value.id === "{" || the_value.id === "[") {
  2670. warn("expected_a_before_b", the_paren, "function", "(");
  2671. return stop("expected_a_b", next_token, "{", "=>");
  2672. }
  2673. return stop("expected_identifier_a", the_value);
  2674. }
  2675. the_paren.expression = [the_value];
  2676. return fart([the_paren.expression, "(" + the_value.id + ")"]);
  2677. }
  2678. return the_value;
  2679. });
  2680. prefix("`", do_tick);
  2681. prefix("{", function () {
  2682. const the_brace = token;
  2683. const seen = empty();
  2684. the_brace.expression = [];
  2685. if (next_token.id !== "}") {
  2686. (function member() {
  2687. let extra;
  2688. let full;
  2689. let id;
  2690. let name = next_token;
  2691. let value;
  2692. advance();
  2693. if (
  2694. (name.id === "get" || name.id === "set")
  2695. && next_token.identifier
  2696. ) {
  2697. if (!option.getset) {
  2698. warn("unexpected_a", name);
  2699. }
  2700. extra = name.id;
  2701. full = extra + " " + next_token.id;
  2702. name = next_token;
  2703. advance();
  2704. id = survey(name);
  2705. if (seen[full] === true || seen[id] === true) {
  2706. warn("duplicate_a", name);
  2707. }
  2708. seen[id] = false;
  2709. seen[full] = true;
  2710. } else {
  2711. id = survey(name);
  2712. if (typeof seen[id] === "boolean") {
  2713. warn("duplicate_a", name);
  2714. }
  2715. seen[id] = true;
  2716. }
  2717. if (name.identifier) {
  2718. if (next_token.id === "}" || next_token.id === ",") {
  2719. if (typeof extra === "string") {
  2720. advance("(");
  2721. }
  2722. value = expression(Infinity, true);
  2723. } else if (next_token.id === "(") {
  2724. value = do_function({
  2725. arity: "unary",
  2726. from: name.from,
  2727. id: "function",
  2728. line: name.line,
  2729. name: (
  2730. typeof extra === "string"
  2731. ? extra
  2732. : id
  2733. ),
  2734. thru: name.from
  2735. });
  2736. } else {
  2737. if (typeof extra === "string") {
  2738. advance("(");
  2739. }
  2740. let the_colon = next_token;
  2741. advance(":");
  2742. value = expression(0);
  2743. if (value.id === name.id && value.id !== "function") {
  2744. warn("unexpected_a", the_colon, ": " + name.id);
  2745. }
  2746. }
  2747. value.label = name;
  2748. if (typeof extra === "string") {
  2749. value.extra = extra;
  2750. }
  2751. the_brace.expression.push(value);
  2752. } else {
  2753. advance(":");
  2754. value = expression(0);
  2755. value.label = name;
  2756. the_brace.expression.push(value);
  2757. }
  2758. if (next_token.id === ",") {
  2759. advance(",");
  2760. return member();
  2761. }
  2762. }());
  2763. }
  2764. advance("}");
  2765. return the_brace;
  2766. });
  2767. stmt(";", function () {
  2768. warn("unexpected_a", token);
  2769. return token;
  2770. });
  2771. stmt("{", function () {
  2772. warn("naked_block", token);
  2773. return block("naked");
  2774. });
  2775. stmt("break", function () {
  2776. const the_break = token;
  2777. let the_label;
  2778. if (
  2779. (functionage.loop < 1 && functionage.switch < 1)
  2780. || functionage.finally > 0
  2781. ) {
  2782. warn("unexpected_a", the_break);
  2783. }
  2784. the_break.disrupt = true;
  2785. if (next_token.identifier && token.line === next_token.line) {
  2786. the_label = functionage.context[next_token.id];
  2787. if (
  2788. the_label === undefined
  2789. || the_label.role !== "label"
  2790. || the_label.dead
  2791. ) {
  2792. warn(
  2793. (the_label !== undefined && the_label.dead)
  2794. ? "out_of_scope_a"
  2795. : "not_label_a"
  2796. );
  2797. } else {
  2798. the_label.used += 1;
  2799. }
  2800. the_break.label = next_token;
  2801. advance();
  2802. }
  2803. advance(";");
  2804. return the_break;
  2805. });
  2806. function do_var() {
  2807. const the_statement = token;
  2808. const is_const = the_statement.id === "const";
  2809. the_statement.names = [];
  2810. // A program may use var or let, but not both.
  2811. if (!is_const) {
  2812. if (var_mode === undefined) {
  2813. var_mode = the_statement.id;
  2814. } else if (the_statement.id !== var_mode) {
  2815. warn(
  2816. "expected_a_b",
  2817. the_statement,
  2818. var_mode,
  2819. the_statement.id
  2820. );
  2821. }
  2822. }
  2823. // We don't expect to see variables created in switch statements.
  2824. if (functionage.switch > 0) {
  2825. warn("var_switch", the_statement);
  2826. }
  2827. if (functionage.loop > 0 && the_statement.id === "var") {
  2828. warn("var_loop", the_statement);
  2829. }
  2830. (function next() {
  2831. if (next_token.id === "{" && the_statement.id !== "var") {
  2832. const the_brace = next_token;
  2833. advance("{");
  2834. (function pair() {
  2835. if (!next_token.identifier) {
  2836. return stop("expected_identifier_a", next_token);
  2837. }
  2838. const name = next_token;
  2839. survey(name);
  2840. advance();
  2841. if (next_token.id === ":") {
  2842. advance(":");
  2843. if (!next_token.identifier) {
  2844. return stop("expected_identifier_a", next_token);
  2845. }
  2846. next_token.label = name;
  2847. the_statement.names.push(next_token);
  2848. enroll(next_token, "variable", is_const);
  2849. advance();
  2850. the_brace.open = true;
  2851. } else {
  2852. the_statement.names.push(name);
  2853. enroll(name, "variable", is_const);
  2854. }
  2855. name.dead = false;
  2856. name.init = true;
  2857. if (next_token.id === "=") {
  2858. advance("=");
  2859. name.expression = expression();
  2860. the_brace.open = true;
  2861. }
  2862. if (next_token.id === ",") {
  2863. advance(",");
  2864. return pair();
  2865. }
  2866. }());
  2867. advance("}");
  2868. advance("=");
  2869. the_statement.expression = expression(0);
  2870. } else if (next_token.id === "[" && the_statement.id !== "var") {
  2871. const the_bracket = next_token;
  2872. advance("[");
  2873. (function element() {
  2874. let ellipsis;
  2875. if (next_token.id === "...") {
  2876. ellipsis = true;
  2877. advance("...");
  2878. }
  2879. if (!next_token.identifier) {
  2880. return stop("expected_identifier_a", next_token);
  2881. }
  2882. const name = next_token;
  2883. advance();
  2884. the_statement.names.push(name);
  2885. enroll(name, "variable", is_const);
  2886. name.dead = false;
  2887. name.init = true;
  2888. if (ellipsis) {
  2889. name.ellipsis = true;
  2890. } else {
  2891. if (next_token.id === "=") {
  2892. advance("=");
  2893. name.expression = expression();
  2894. the_bracket.open = true;
  2895. }
  2896. if (next_token.id === ",") {
  2897. advance(",");
  2898. return element();
  2899. }
  2900. }
  2901. }());
  2902. advance("]");
  2903. advance("=");
  2904. the_statement.expression = expression(0);
  2905. } else if (next_token.identifier) {
  2906. const name = next_token;
  2907. advance();
  2908. if (name.id === "ignore") {
  2909. warn("unexpected_a", name);
  2910. }
  2911. enroll(name, "variable", is_const);
  2912. if (next_token.id === "=" || is_const) {
  2913. advance("=");
  2914. name.dead = false;
  2915. name.init = true;
  2916. name.expression = expression(0);
  2917. }
  2918. the_statement.names.push(name);
  2919. } else {
  2920. return stop("expected_identifier_a", next_token);
  2921. }
  2922. }());
  2923. semicolon();
  2924. return the_statement;
  2925. }
  2926. stmt("const", do_var);
  2927. stmt("continue", function () {
  2928. const the_continue = token;
  2929. if (functionage.loop < 1 || functionage.finally > 0) {
  2930. warn("unexpected_a", the_continue);
  2931. }
  2932. not_top_level(the_continue);
  2933. the_continue.disrupt = true;
  2934. warn("unexpected_a", the_continue);
  2935. advance(";");
  2936. return the_continue;
  2937. });
  2938. stmt("debugger", function () {
  2939. const the_debug = token;
  2940. if (!option.devel) {
  2941. warn("unexpected_a", the_debug);
  2942. }
  2943. semicolon();
  2944. return the_debug;
  2945. });
  2946. stmt("delete", function () {
  2947. const the_token = token;
  2948. const the_value = expression(0);
  2949. if (
  2950. (the_value.id !== "." && the_value.id !== "[")
  2951. || the_value.arity !== "binary"
  2952. ) {
  2953. stop("expected_a_b", the_value, ".", artifact(the_value));
  2954. }
  2955. the_token.expression = the_value;
  2956. semicolon();
  2957. return the_token;
  2958. });
  2959. stmt("do", function () {
  2960. const the_do = token;
  2961. not_top_level(the_do);
  2962. functionage.loop += 1;
  2963. the_do.block = block();
  2964. advance("while");
  2965. the_do.expression = condition();
  2966. semicolon();
  2967. if (the_do.block.disrupt === true) {
  2968. warn("weird_loop", the_do);
  2969. }
  2970. functionage.loop -= 1;
  2971. return the_do;
  2972. });
  2973. stmt("export", function () {
  2974. const the_export = token;
  2975. let the_id;
  2976. let the_name;
  2977. let the_thing;
  2978. function export_id() {
  2979. if (!next_token.identifier) {
  2980. stop("expected_identifier_a");
  2981. }
  2982. the_id = next_token.id;
  2983. the_name = global.context[the_id];
  2984. if (the_name === undefined) {
  2985. warn("unexpected_a");
  2986. } else {
  2987. the_name.used += 1;
  2988. if (exports[the_id] !== undefined) {
  2989. warn("duplicate_a");
  2990. }
  2991. exports[the_id] = the_name;
  2992. }
  2993. advance();
  2994. the_export.expression.push(the_thing);
  2995. }
  2996. the_export.expression = [];
  2997. if (next_token.id === "default") {
  2998. if (exports.default !== undefined) {
  2999. warn("duplicate_a");
  3000. }
  3001. advance("default");
  3002. the_thing = expression(0);
  3003. if (
  3004. the_thing.id !== "("
  3005. || the_thing.expression[0].id !== "."
  3006. || the_thing.expression[0].expression.id !== "Object"
  3007. || the_thing.expression[0].name.id !== "freeze"
  3008. ) {
  3009. warn("freeze_exports", the_thing);
  3010. }
  3011. if (next_token.id === ";") {
  3012. semicolon();
  3013. }
  3014. exports.default = the_thing;
  3015. the_export.expression.push(the_thing);
  3016. } else {
  3017. if (next_token.id === "function") {
  3018. warn("freeze_exports");
  3019. the_thing = statement();
  3020. the_name = the_thing.name;
  3021. the_id = the_name.id;
  3022. the_name.used += 1;
  3023. if (exports[the_id] !== undefined) {
  3024. warn("duplicate_a", the_name);
  3025. }
  3026. exports[the_id] = the_thing;
  3027. the_export.expression.push(the_thing);
  3028. the_thing.statement = false;
  3029. the_thing.arity = "unary";
  3030. } else if (
  3031. next_token.id === "var"
  3032. || next_token.id === "let"
  3033. || next_token.id === "const"
  3034. ) {
  3035. warn("unexpected_a", next_token);
  3036. statement();
  3037. } else if (next_token.id === "{") {
  3038. advance("{");
  3039. (function loop() {
  3040. export_id();
  3041. if (next_token.id === ",") {
  3042. advance(",");
  3043. return loop();
  3044. }
  3045. }());
  3046. advance("}");
  3047. semicolon();
  3048. } else {
  3049. stop("unexpected_a");
  3050. }
  3051. }
  3052. module_mode = true;
  3053. return the_export;
  3054. });
  3055. stmt("for", function () {
  3056. let first;
  3057. const the_for = token;
  3058. if (!option.for) {
  3059. warn("unexpected_a", the_for);
  3060. }
  3061. not_top_level(the_for);
  3062. functionage.loop += 1;
  3063. advance("(");
  3064. token.free = true;
  3065. if (next_token.id === ";") {
  3066. return stop("expected_a_b", the_for, "while (", "for (;");
  3067. }
  3068. if (
  3069. next_token.id === "var"
  3070. || next_token.id === "let"
  3071. || next_token.id === "const"
  3072. ) {
  3073. return stop("unexpected_a");
  3074. }
  3075. first = expression(0);
  3076. if (first.id === "in") {
  3077. if (first.expression[0].arity !== "variable") {
  3078. warn("bad_assignment_a", first.expression[0]);
  3079. }
  3080. the_for.name = first.expression[0];
  3081. the_for.expression = first.expression[1];
  3082. warn("expected_a_b", the_for, "Object.keys", "for in");
  3083. } else {
  3084. the_for.initial = first;
  3085. advance(";");
  3086. the_for.expression = expression(0);
  3087. advance(";");
  3088. the_for.inc = expression(0);
  3089. if (the_for.inc.id === "++") {
  3090. warn("expected_a_b", the_for.inc, "+= 1", "++");
  3091. }
  3092. }
  3093. advance(")");
  3094. the_for.block = block();
  3095. if (the_for.block.disrupt === true) {
  3096. warn("weird_loop", the_for);
  3097. }
  3098. functionage.loop -= 1;
  3099. return the_for;
  3100. });
  3101. stmt("function", do_function);
  3102. stmt("if", function () {
  3103. let the_else;
  3104. const the_if = token;
  3105. the_if.expression = condition();
  3106. the_if.block = block();
  3107. if (next_token.id === "else") {
  3108. advance("else");
  3109. the_else = token;
  3110. the_if.else = (
  3111. next_token.id === "if"
  3112. ? statement()
  3113. : block()
  3114. );
  3115. if (the_if.block.disrupt === true) {
  3116. if (the_if.else.disrupt === true) {
  3117. the_if.disrupt = true;
  3118. } else {
  3119. warn("unexpected_a", the_else);
  3120. }
  3121. }
  3122. }
  3123. return the_if;
  3124. });
  3125. stmt("import", function () {
  3126. const the_import = token;
  3127. if (next_token.id === "(") {
  3128. the_import.arity = "unary";
  3129. the_import.constant = true;
  3130. the_import.statement = false;
  3131. advance("(");
  3132. const string = expression(0);
  3133. if (string.id !== "(string)") {
  3134. warn("expected_string_a", string);
  3135. }
  3136. froms.push(token.value);
  3137. advance(")");
  3138. advance(".");
  3139. advance("then");
  3140. advance("(");
  3141. the_import.expression = expression(0);
  3142. advance(")");
  3143. semicolon();
  3144. return the_import;
  3145. }
  3146. let name;
  3147. if (typeof module_mode === "object") {
  3148. warn("unexpected_directive_a", module_mode, module_mode.directive);
  3149. }
  3150. module_mode = true;
  3151. if (next_token.identifier) {
  3152. name = next_token;
  3153. advance();
  3154. if (name.id === "ignore") {
  3155. warn("unexpected_a", name);
  3156. }
  3157. enroll(name, "variable", true);
  3158. the_import.name = name;
  3159. } else {
  3160. const names = [];
  3161. advance("{");
  3162. if (next_token.id !== "}") {
  3163. while (true) {
  3164. if (!next_token.identifier) {
  3165. stop("expected_identifier_a");
  3166. }
  3167. name = next_token;
  3168. advance();
  3169. if (name.id === "ignore") {
  3170. warn("unexpected_a", name);
  3171. }
  3172. enroll(name, "variable", true);
  3173. names.push(name);
  3174. if (next_token.id !== ",") {
  3175. break;
  3176. }
  3177. advance(",");
  3178. }
  3179. }
  3180. advance("}");
  3181. the_import.name = names;
  3182. }
  3183. advance("from");
  3184. advance("(string)");
  3185. the_import.import = token;
  3186. if (!rx_module.test(token.value)) {
  3187. warn("bad_module_name_a", token);
  3188. }
  3189. froms.push(token.value);
  3190. semicolon();
  3191. return the_import;
  3192. });
  3193. stmt("let", do_var);
  3194. stmt("return", function () {
  3195. const the_return = token;
  3196. not_top_level(the_return);
  3197. if (functionage.finally > 0) {
  3198. warn("unexpected_a", the_return);
  3199. }
  3200. the_return.disrupt = true;
  3201. if (next_token.id !== ";" && the_return.line === next_token.line) {
  3202. the_return.expression = expression(10);
  3203. }
  3204. advance(";");
  3205. return the_return;
  3206. });
  3207. stmt("switch", function () {
  3208. let dups = [];
  3209. let last;
  3210. let stmts;
  3211. const the_cases = [];
  3212. let the_disrupt = true;
  3213. const the_switch = token;
  3214. not_top_level(the_switch);
  3215. if (functionage.finally > 0) {
  3216. warn("unexpected_a", the_switch);
  3217. }
  3218. functionage.switch += 1;
  3219. advance("(");
  3220. token.free = true;
  3221. the_switch.expression = expression(0);
  3222. the_switch.block = the_cases;
  3223. advance(")");
  3224. advance("{");
  3225. (function major() {
  3226. const the_case = next_token;
  3227. the_case.arity = "statement";
  3228. the_case.expression = [];
  3229. (function minor() {
  3230. advance("case");
  3231. token.switch = true;
  3232. const exp = expression(0);
  3233. if (dups.some(function (thing) {
  3234. return are_similar(thing, exp);
  3235. })) {
  3236. warn("unexpected_a", exp);
  3237. }
  3238. dups.push(exp);
  3239. the_case.expression.push(exp);
  3240. advance(":");
  3241. if (next_token.id === "case") {
  3242. return minor();
  3243. }
  3244. }());
  3245. stmts = statements();
  3246. if (stmts.length < 1) {
  3247. warn("expected_statements_a");
  3248. return;
  3249. }
  3250. the_case.block = stmts;
  3251. the_cases.push(the_case);
  3252. last = stmts[stmts.length - 1];
  3253. if (last.disrupt) {
  3254. if (last.id === "break" && last.label === undefined) {
  3255. the_disrupt = false;
  3256. }
  3257. } else {
  3258. warn(
  3259. "expected_a_before_b",
  3260. next_token,
  3261. "break;",
  3262. artifact(next_token)
  3263. );
  3264. }
  3265. if (next_token.id === "case") {
  3266. return major();
  3267. }
  3268. }());
  3269. dups = undefined;
  3270. if (next_token.id === "default") {
  3271. const the_default = next_token;
  3272. advance("default");
  3273. token.switch = true;
  3274. advance(":");
  3275. the_switch.else = statements();
  3276. if (the_switch.else.length < 1) {
  3277. warn("unexpected_a", the_default);
  3278. the_disrupt = false;
  3279. } else {
  3280. const the_last = the_switch.else[the_switch.else.length - 1];
  3281. if (the_last.id === "break" && the_last.label === undefined) {
  3282. warn("unexpected_a", the_last);
  3283. the_last.disrupt = false;
  3284. }
  3285. the_disrupt = the_disrupt && the_last.disrupt;
  3286. }
  3287. } else {
  3288. the_disrupt = false;
  3289. }
  3290. advance("}", the_switch);
  3291. functionage.switch -= 1;
  3292. the_switch.disrupt = the_disrupt;
  3293. return the_switch;
  3294. });
  3295. stmt("throw", function () {
  3296. const the_throw = token;
  3297. the_throw.disrupt = true;
  3298. the_throw.expression = expression(10);
  3299. semicolon();
  3300. if (functionage.try > 0) {
  3301. warn("unexpected_a", the_throw);
  3302. }
  3303. return the_throw;
  3304. });
  3305. stmt("try", function () {
  3306. let the_catch;
  3307. let the_disrupt;
  3308. const the_try = token;
  3309. if (functionage.try > 0) {
  3310. warn("unexpected_a", the_try);
  3311. }
  3312. functionage.try += 1;
  3313. the_try.block = block();
  3314. the_disrupt = the_try.block.disrupt;
  3315. if (next_token.id === "catch") {
  3316. let ignored = "ignore";
  3317. the_catch = next_token;
  3318. the_try.catch = the_catch;
  3319. advance("catch");
  3320. if (next_token.id === "(") {
  3321. advance("(");
  3322. if (!next_token.identifier) {
  3323. return stop("expected_identifier_a", next_token);
  3324. }
  3325. if (next_token.id !== "ignore") {
  3326. ignored = undefined;
  3327. the_catch.name = next_token;
  3328. enroll(next_token, "exception", true);
  3329. }
  3330. advance();
  3331. advance(")");
  3332. }
  3333. the_catch.block = block(ignored);
  3334. if (the_catch.block.disrupt !== true) {
  3335. the_disrupt = false;
  3336. }
  3337. } else {
  3338. warn(
  3339. "expected_a_before_b",
  3340. next_token,
  3341. "catch",
  3342. artifact(next_token)
  3343. );
  3344. }
  3345. if (next_token.id === "finally") {
  3346. functionage.finally += 1;
  3347. advance("finally");
  3348. the_try.else = block();
  3349. the_disrupt = the_try.else.disrupt;
  3350. functionage.finally -= 1;
  3351. }
  3352. the_try.disrupt = the_disrupt;
  3353. functionage.try -= 1;
  3354. return the_try;
  3355. });
  3356. stmt("var", do_var);
  3357. stmt("while", function () {
  3358. const the_while = token;
  3359. not_top_level(the_while);
  3360. functionage.loop += 1;
  3361. the_while.expression = condition();
  3362. the_while.block = block();
  3363. if (the_while.block.disrupt === true) {
  3364. warn("weird_loop", the_while);
  3365. }
  3366. functionage.loop -= 1;
  3367. return the_while;
  3368. });
  3369. stmt("with", function () {
  3370. stop("unexpected_a", token);
  3371. });
  3372. ternary("?", ":");
  3373. // Ambulation of the parse tree.
  3374. function action(when) {
  3375. // Produce a function that will register task functions that will be called as
  3376. // the tree is traversed.
  3377. return function (arity, id, task) {
  3378. let a_set = when[arity];
  3379. let i_set;
  3380. // The id parameter is optional. If excluded, the task will be applied to all
  3381. // ids.
  3382. if (typeof id !== "string") {
  3383. task = id;
  3384. id = "(all)";
  3385. }
  3386. // If this arity has no registrations yet, then create a set object to hold
  3387. // them.
  3388. if (a_set === undefined) {
  3389. a_set = empty();
  3390. when[arity] = a_set;
  3391. }
  3392. // If this id has no registrations yet, then create a set array to hold them.
  3393. i_set = a_set[id];
  3394. if (i_set === undefined) {
  3395. i_set = [];
  3396. a_set[id] = i_set;
  3397. }
  3398. // Register the task with the arity and the id.
  3399. i_set.push(task);
  3400. };
  3401. }
  3402. function amble(when) {
  3403. // Produce a function that will act on the tasks registered by an action
  3404. // function while walking the tree.
  3405. return function (the_token) {
  3406. // Given a task set that was built by an action function, run all of the
  3407. // relevant tasks on the token.
  3408. let a_set = when[the_token.arity];
  3409. let i_set;
  3410. // If there are tasks associated with the token's arity...
  3411. if (a_set !== undefined) {
  3412. // If there are tasks associated with the token's id...
  3413. i_set = a_set[the_token.id];
  3414. if (i_set !== undefined) {
  3415. i_set.forEach(function (task) {
  3416. return task(the_token);
  3417. });
  3418. }
  3419. // If there are tasks for all ids.
  3420. i_set = a_set["(all)"];
  3421. if (i_set !== undefined) {
  3422. i_set.forEach(function (task) {
  3423. return task(the_token);
  3424. });
  3425. }
  3426. }
  3427. };
  3428. }
  3429. const posts = empty();
  3430. const pres = empty();
  3431. const preaction = action(pres);
  3432. const postaction = action(posts);
  3433. const preamble = amble(pres);
  3434. const postamble = amble(posts);
  3435. function walk_expression(thing) {
  3436. if (thing) {
  3437. if (Array.isArray(thing)) {
  3438. thing.forEach(walk_expression);
  3439. } else {
  3440. preamble(thing);
  3441. walk_expression(thing.expression);
  3442. if (thing.id === "function") {
  3443. walk_statement(thing.block);
  3444. }
  3445. if (thing.arity === "pre" || thing.arity === "post") {
  3446. warn("unexpected_a", thing);
  3447. } else if (
  3448. thing.arity === "statement"
  3449. || thing.arity === "assignment"
  3450. ) {
  3451. warn("unexpected_statement_a", thing);
  3452. }
  3453. postamble(thing);
  3454. }
  3455. }
  3456. }
  3457. function walk_statement(thing) {
  3458. if (thing) {
  3459. if (Array.isArray(thing)) {
  3460. thing.forEach(walk_statement);
  3461. } else {
  3462. preamble(thing);
  3463. walk_expression(thing.expression);
  3464. if (thing.arity === "binary") {
  3465. if (thing.id !== "(") {
  3466. warn("unexpected_expression_a", thing);
  3467. }
  3468. } else if (
  3469. thing.arity !== "statement"
  3470. && thing.arity !== "assignment"
  3471. && thing.id !== "import"
  3472. ) {
  3473. warn("unexpected_expression_a", thing);
  3474. }
  3475. walk_statement(thing.block);
  3476. walk_statement(thing.else);
  3477. postamble(thing);
  3478. }
  3479. }
  3480. }
  3481. function lookup(thing) {
  3482. if (thing.arity === "variable") {
  3483. // Look up the variable in the current context.
  3484. let the_variable = functionage.context[thing.id];
  3485. // If it isn't local, search all the other contexts. If there are name
  3486. // collisions, take the most recent.
  3487. if (the_variable === undefined) {
  3488. stack.forEach(function (outer) {
  3489. const a_variable = outer.context[thing.id];
  3490. if (
  3491. a_variable !== undefined
  3492. && a_variable.role !== "label"
  3493. ) {
  3494. the_variable = a_variable;
  3495. }
  3496. });
  3497. // If it isn't in any of those either, perhaps it is a predefined global.
  3498. // If so, add it to the global context.
  3499. if (the_variable === undefined) {
  3500. if (declared_globals[thing.id] === undefined) {
  3501. warn("undeclared_a", thing);
  3502. return;
  3503. }
  3504. the_variable = {
  3505. dead: false,
  3506. parent: global,
  3507. id: thing.id,
  3508. init: true,
  3509. role: "variable",
  3510. used: 0,
  3511. writable: false
  3512. };
  3513. global.context[thing.id] = the_variable;
  3514. }
  3515. the_variable.closure = true;
  3516. functionage.context[thing.id] = the_variable;
  3517. } else if (the_variable.role === "label") {
  3518. warn("label_a", thing);
  3519. }
  3520. if (
  3521. the_variable.dead
  3522. && (
  3523. the_variable.calls === undefined
  3524. || the_variable.calls[functionage.name.id] === undefined
  3525. )
  3526. ) {
  3527. warn("out_of_scope_a", thing);
  3528. }
  3529. return the_variable;
  3530. }
  3531. }
  3532. function subactivate(name) {
  3533. name.init = true;
  3534. name.dead = false;
  3535. blockage.live.push(name);
  3536. }
  3537. function preaction_function(thing) {
  3538. if (thing.arity === "statement" && blockage.body !== true) {
  3539. warn("unexpected_a", thing);
  3540. }
  3541. stack.push(functionage);
  3542. block_stack.push(blockage);
  3543. functionage = thing;
  3544. blockage = thing;
  3545. thing.live = [];
  3546. if (typeof thing.name === "object") {
  3547. thing.name.dead = false;
  3548. thing.name.init = true;
  3549. }
  3550. if (thing.extra === "get") {
  3551. if (thing.parameters.length !== 0) {
  3552. warn("bad_get", thing);
  3553. }
  3554. } else if (thing.extra === "set") {
  3555. if (thing.parameters.length !== 1) {
  3556. warn("bad_set", thing);
  3557. }
  3558. }
  3559. thing.parameters.forEach(function (name) {
  3560. walk_expression(name.expression);
  3561. if (name.id === "{" || name.id === "[") {
  3562. name.names.forEach(subactivate);
  3563. } else {
  3564. name.dead = false;
  3565. name.init = true;
  3566. }
  3567. });
  3568. }
  3569. function bitwise_check(thing) {
  3570. if (!option.bitwise && bitwiseop[thing.id] === true) {
  3571. warn("unexpected_a", thing);
  3572. }
  3573. if (
  3574. thing.id !== "("
  3575. && thing.id !== "&&"
  3576. && thing.id !== "||"
  3577. && thing.id !== "="
  3578. && Array.isArray(thing.expression)
  3579. && thing.expression.length === 2
  3580. && (
  3581. relationop[thing.expression[0].id] === true
  3582. || relationop[thing.expression[1].id] === true
  3583. )
  3584. ) {
  3585. warn("unexpected_a", thing);
  3586. }
  3587. }
  3588. function pop_block() {
  3589. blockage.live.forEach(function (name) {
  3590. name.dead = true;
  3591. });
  3592. delete blockage.live;
  3593. blockage = block_stack.pop();
  3594. }
  3595. function activate(name) {
  3596. name.dead = false;
  3597. if (name.expression !== undefined) {
  3598. walk_expression(name.expression);
  3599. if (name.id === "{" || name.id === "[") {
  3600. name.names.forEach(subactivate);
  3601. } else {
  3602. name.init = true;
  3603. }
  3604. }
  3605. blockage.live.push(name);
  3606. }
  3607. function action_var(thing) {
  3608. thing.names.forEach(activate);
  3609. }
  3610. preaction("assignment", bitwise_check);
  3611. preaction("binary", bitwise_check);
  3612. preaction("binary", function (thing) {
  3613. if (relationop[thing.id] === true) {
  3614. const left = thing.expression[0];
  3615. const right = thing.expression[1];
  3616. if (left.id === "NaN" || right.id === "NaN") {
  3617. warn("number_isNaN", thing);
  3618. } else if (left.id === "typeof") {
  3619. if (right.id !== "(string)") {
  3620. if (right.id !== "typeof") {
  3621. warn("expected_string_a", right);
  3622. }
  3623. } else {
  3624. const value = right.value;
  3625. if (value === "null" || value === "undefined") {
  3626. warn("unexpected_typeof_a", right, value);
  3627. } else if (
  3628. value !== "boolean"
  3629. && value !== "function"
  3630. && value !== "number"
  3631. && value !== "object"
  3632. && value !== "string"
  3633. && value !== "symbol"
  3634. ) {
  3635. warn("expected_type_string_a", right, value);
  3636. }
  3637. }
  3638. }
  3639. }
  3640. });
  3641. preaction("binary", "==", function (thing) {
  3642. warn("expected_a_b", thing, "===", "==");
  3643. });
  3644. preaction("binary", "!=", function (thing) {
  3645. warn("expected_a_b", thing, "!==", "!=");
  3646. });
  3647. preaction("binary", "=>", preaction_function);
  3648. preaction("binary", "||", function (thing) {
  3649. thing.expression.forEach(function (thang) {
  3650. if (thang.id === "&&" && !thang.wrapped) {
  3651. warn("and", thang);
  3652. }
  3653. });
  3654. });
  3655. preaction("binary", "(", function (thing) {
  3656. const left = thing.expression[0];
  3657. if (
  3658. left.identifier
  3659. && functionage.context[left.id] === undefined
  3660. && typeof functionage.name === "object"
  3661. ) {
  3662. const parent = functionage.name.parent;
  3663. if (parent) {
  3664. const left_variable = parent.context[left.id];
  3665. if (
  3666. left_variable !== undefined
  3667. && left_variable.dead
  3668. && left_variable.parent === parent
  3669. && left_variable.calls !== undefined
  3670. && left_variable.calls[functionage.name.id] !== undefined
  3671. ) {
  3672. left_variable.dead = false;
  3673. }
  3674. }
  3675. }
  3676. });
  3677. preaction("binary", "in", function (thing) {
  3678. warn("infix_in", thing);
  3679. });
  3680. preaction("binary", "instanceof", function (thing) {
  3681. warn("unexpected_a", thing);
  3682. });
  3683. preaction("binary", ".", function (thing) {
  3684. if (thing.expression.new) {
  3685. thing.new = true;
  3686. }
  3687. });
  3688. preaction("statement", "{", function (thing) {
  3689. block_stack.push(blockage);
  3690. blockage = thing;
  3691. thing.live = [];
  3692. });
  3693. preaction("statement", "for", function (thing) {
  3694. if (thing.name !== undefined) {
  3695. const the_variable = lookup(thing.name);
  3696. if (the_variable !== undefined) {
  3697. the_variable.init = true;
  3698. if (!the_variable.writable) {
  3699. warn("bad_assignment_a", thing.name);
  3700. }
  3701. }
  3702. }
  3703. walk_statement(thing.initial);
  3704. });
  3705. preaction("statement", "function", preaction_function);
  3706. preaction("unary", "~", bitwise_check);
  3707. preaction("unary", "function", preaction_function);
  3708. preaction("variable", function (thing) {
  3709. const the_variable = lookup(thing);
  3710. if (the_variable !== undefined) {
  3711. thing.variable = the_variable;
  3712. the_variable.used += 1;
  3713. }
  3714. });
  3715. function init_variable(name) {
  3716. const the_variable = lookup(name);
  3717. if (the_variable !== undefined) {
  3718. if (the_variable.writable) {
  3719. the_variable.init = true;
  3720. return;
  3721. }
  3722. }
  3723. warn("bad_assignment_a", name);
  3724. }
  3725. postaction("assignment", "+=", function (thing) {
  3726. let right = thing.expression[1];
  3727. if (right.constant) {
  3728. if (
  3729. right.value === ""
  3730. || (right.id === "(number)" && right.value === "0")
  3731. || right.id === "(boolean)"
  3732. || right.id === "null"
  3733. || right.id === "undefined"
  3734. || Number.isNaN(right.value)
  3735. ) {
  3736. warn("unexpected_a", right);
  3737. }
  3738. }
  3739. });
  3740. postaction("assignment", function (thing) {
  3741. // Assignment using = sets the init property of a variable. No other assignment
  3742. // operator can do this. A = token keeps that variable (or array of variables
  3743. // in case of destructuring) in its name property.
  3744. const lvalue = thing.expression[0];
  3745. if (thing.id === "=") {
  3746. if (thing.names !== undefined) {
  3747. if (Array.isArray(thing.names)) {
  3748. thing.names.forEach(init_variable);
  3749. } else {
  3750. init_variable(thing.names);
  3751. }
  3752. } else {
  3753. if (lvalue.id === "[" || lvalue.id === "{") {
  3754. lvalue.expression.forEach(function (thing) {
  3755. if (thing.variable) {
  3756. thing.variable.init = true;
  3757. }
  3758. });
  3759. } else if (
  3760. lvalue.id === "."
  3761. && thing.expression[1].id === "undefined"
  3762. ) {
  3763. warn(
  3764. "expected_a_b",
  3765. lvalue.expression,
  3766. "delete",
  3767. "undefined"
  3768. );
  3769. }
  3770. }
  3771. } else {
  3772. if (lvalue.arity === "variable") {
  3773. if (!lvalue.variable || lvalue.variable.writable !== true) {
  3774. warn("bad_assignment_a", lvalue);
  3775. }
  3776. }
  3777. const right = syntax[thing.expression[1].id];
  3778. if (
  3779. right !== undefined
  3780. && (
  3781. right.id === "function"
  3782. || right.id === "=>"
  3783. || (
  3784. right.constant
  3785. && right.id !== "(number)"
  3786. && (right.id !== "(string)" || thing.id !== "+=")
  3787. )
  3788. )
  3789. ) {
  3790. warn("unexpected_a", thing.expression[1]);
  3791. }
  3792. }
  3793. });
  3794. function postaction_function(thing) {
  3795. delete functionage.finally;
  3796. delete functionage.loop;
  3797. delete functionage.switch;
  3798. delete functionage.try;
  3799. functionage = stack.pop();
  3800. if (thing.wrapped) {
  3801. warn("unexpected_parens", thing);
  3802. }
  3803. return pop_block();
  3804. }
  3805. postaction("binary", function (thing) {
  3806. let right;
  3807. if (relationop[thing.id]) {
  3808. if (
  3809. is_weird(thing.expression[0])
  3810. || is_weird(thing.expression[1])
  3811. || are_similar(thing.expression[0], thing.expression[1])
  3812. || (
  3813. thing.expression[0].constant === true
  3814. && thing.expression[1].constant === true
  3815. )
  3816. ) {
  3817. warn("weird_relation_a", thing);
  3818. }
  3819. }
  3820. if (thing.id === "+") {
  3821. if (!option.convert) {
  3822. if (thing.expression[0].value === "") {
  3823. warn("expected_a_b", thing, "String(...)", "\"\" +");
  3824. } else if (thing.expression[1].value === "") {
  3825. warn("expected_a_b", thing, "String(...)", "+ \"\"");
  3826. }
  3827. }
  3828. } else if (thing.id === "[") {
  3829. if (thing.expression[0].id === "window") {
  3830. warn("weird_expression_a", thing, "window[...]");
  3831. }
  3832. if (thing.expression[0].id === "self") {
  3833. warn("weird_expression_a", thing, "self[...]");
  3834. }
  3835. } else if (thing.id === "." || thing.id === "?.") {
  3836. if (thing.expression.id === "RegExp") {
  3837. warn("weird_expression_a", thing);
  3838. }
  3839. } else if (thing.id !== "=>" && thing.id !== "(") {
  3840. right = thing.expression[1];
  3841. if (
  3842. (thing.id === "+" || thing.id === "-")
  3843. && right.id === thing.id
  3844. && right.arity === "unary"
  3845. && !right.wrapped
  3846. ) {
  3847. warn("wrap_unary", right);
  3848. }
  3849. if (
  3850. thing.expression[0].constant === true
  3851. && right.constant === true
  3852. ) {
  3853. thing.constant = true;
  3854. }
  3855. }
  3856. });
  3857. postaction("binary", "&&", function (thing) {
  3858. if (
  3859. is_weird(thing.expression[0])
  3860. || are_similar(thing.expression[0], thing.expression[1])
  3861. || thing.expression[0].constant === true
  3862. || thing.expression[1].constant === true
  3863. ) {
  3864. warn("weird_condition_a", thing);
  3865. }
  3866. });
  3867. postaction("binary", "||", function (thing) {
  3868. if (
  3869. is_weird(thing.expression[0])
  3870. || are_similar(thing.expression[0], thing.expression[1])
  3871. || thing.expression[0].constant === true
  3872. ) {
  3873. warn("weird_condition_a", thing);
  3874. }
  3875. });
  3876. postaction("binary", "=>", postaction_function);
  3877. postaction("binary", "(", function (thing) {
  3878. let left = thing.expression[0];
  3879. let the_new;
  3880. let arg;
  3881. if (left.id === "new") {
  3882. the_new = left;
  3883. left = left.expression;
  3884. }
  3885. if (left.id === "function") {
  3886. if (!thing.wrapped) {
  3887. warn("wrap_immediate", thing);
  3888. }
  3889. } else if (left.identifier) {
  3890. if (the_new !== undefined) {
  3891. if (
  3892. left.id[0] > "Z"
  3893. || left.id === "Boolean"
  3894. || left.id === "Number"
  3895. || left.id === "String"
  3896. || left.id === "Symbol"
  3897. ) {
  3898. warn("unexpected_a", the_new);
  3899. } else if (left.id === "Function") {
  3900. if (!option.eval) {
  3901. warn("unexpected_a", left, "new Function");
  3902. }
  3903. } else if (left.id === "Array") {
  3904. arg = thing.expression;
  3905. if (arg.length !== 2 || arg[1].id === "(string)") {
  3906. warn("expected_a_b", left, "[]", "new Array");
  3907. }
  3908. } else if (left.id === "Object") {
  3909. warn(
  3910. "expected_a_b",
  3911. left,
  3912. "Object.create(null)",
  3913. "new Object"
  3914. );
  3915. }
  3916. } else {
  3917. if (
  3918. left.id[0] >= "A"
  3919. && left.id[0] <= "Z"
  3920. && left.id !== "Boolean"
  3921. && left.id !== "Number"
  3922. && left.id !== "String"
  3923. && left.id !== "Symbol"
  3924. ) {
  3925. warn(
  3926. "expected_a_before_b",
  3927. left,
  3928. "new",
  3929. artifact(left)
  3930. );
  3931. }
  3932. }
  3933. } else if (left.id === ".") {
  3934. let cack = the_new !== undefined;
  3935. if (left.expression.id === "Date" && left.name.id === "UTC") {
  3936. cack = !cack;
  3937. }
  3938. if (rx_cap.test(left.name.id) !== cack) {
  3939. if (the_new !== undefined) {
  3940. warn("unexpected_a", the_new);
  3941. } else {
  3942. warn(
  3943. "expected_a_before_b",
  3944. left.expression,
  3945. "new",
  3946. left.name.id
  3947. );
  3948. }
  3949. }
  3950. if (left.name.id === "getTime") {
  3951. const paren = left.expression;
  3952. if (paren.id === "(") {
  3953. const array = paren.expression;
  3954. if (array.length === 1) {
  3955. const new_date = array[0];
  3956. if (
  3957. new_date.id === "new"
  3958. && new_date.expression.id === "Date"
  3959. ) {
  3960. warn(
  3961. "expected_a_b",
  3962. new_date,
  3963. "Date.now()",
  3964. "new Date().getTime()"
  3965. );
  3966. }
  3967. }
  3968. }
  3969. }
  3970. }
  3971. });
  3972. postaction("binary", "[", function (thing) {
  3973. if (thing.expression[0].id === "RegExp") {
  3974. warn("weird_expression_a", thing);
  3975. }
  3976. if (is_weird(thing.expression[1])) {
  3977. warn("weird_expression_a", thing.expression[1]);
  3978. }
  3979. });
  3980. postaction("statement", "{", pop_block);
  3981. postaction("statement", "const", action_var);
  3982. postaction("statement", "export", top_level_only);
  3983. postaction("statement", "for", function (thing) {
  3984. walk_statement(thing.inc);
  3985. });
  3986. postaction("statement", "function", postaction_function);
  3987. postaction("statement", "import", function (the_thing) {
  3988. const name = the_thing.name;
  3989. if (name) {
  3990. if (Array.isArray(name)) {
  3991. name.forEach(function (name) {
  3992. name.dead = false;
  3993. name.init = true;
  3994. blockage.live.push(name);
  3995. });
  3996. } else {
  3997. name.dead = false;
  3998. name.init = true;
  3999. blockage.live.push(name);
  4000. }
  4001. return top_level_only(the_thing);
  4002. }
  4003. });
  4004. postaction("statement", "let", action_var);
  4005. postaction("statement", "try", function (thing) {
  4006. if (thing.catch !== undefined) {
  4007. const the_name = thing.catch.name;
  4008. if (the_name !== undefined) {
  4009. const the_variable = functionage.context[the_name.id];
  4010. the_variable.dead = false;
  4011. the_variable.init = true;
  4012. }
  4013. walk_statement(thing.catch.block);
  4014. }
  4015. });
  4016. postaction("statement", "var", action_var);
  4017. postaction("ternary", function (thing) {
  4018. if (
  4019. is_weird(thing.expression[0])
  4020. || thing.expression[0].constant === true
  4021. || are_similar(thing.expression[1], thing.expression[2])
  4022. ) {
  4023. warn("unexpected_a", thing);
  4024. } else if (are_similar(thing.expression[0], thing.expression[1])) {
  4025. warn("expected_a_b", thing, "||", "?");
  4026. } else if (are_similar(thing.expression[0], thing.expression[2])) {
  4027. warn("expected_a_b", thing, "&&", "?");
  4028. } else if (
  4029. thing.expression[1].id === "true"
  4030. && thing.expression[2].id === "false"
  4031. ) {
  4032. warn("expected_a_b", thing, "!!", "?");
  4033. } else if (
  4034. thing.expression[1].id === "false"
  4035. && thing.expression[2].id === "true"
  4036. ) {
  4037. warn("expected_a_b", thing, "!", "?");
  4038. } else if (
  4039. thing.expression[0].wrapped !== true
  4040. && (
  4041. thing.expression[0].id === "||"
  4042. || thing.expression[0].id === "&&"
  4043. )
  4044. ) {
  4045. warn("wrap_condition", thing.expression[0]);
  4046. }
  4047. });
  4048. postaction("unary", function (thing) {
  4049. if (thing.id === "`") {
  4050. if (thing.expression.every(function (thing) {
  4051. return thing.constant;
  4052. })) {
  4053. thing.constant = true;
  4054. }
  4055. } else if (thing.id === "!") {
  4056. if (thing.expression.constant === true) {
  4057. warn("unexpected_a", thing);
  4058. }
  4059. } else if (thing.id === "!!") {
  4060. if (!option.convert) {
  4061. warn("expected_a_b", thing, "Boolean(...)", "!!");
  4062. }
  4063. } else if (
  4064. thing.id !== "["
  4065. && thing.id !== "{"
  4066. && thing.id !== "function"
  4067. && thing.id !== "new"
  4068. ) {
  4069. if (thing.expression.constant === true) {
  4070. thing.constant = true;
  4071. }
  4072. }
  4073. });
  4074. postaction("unary", "function", postaction_function);
  4075. postaction("unary", "+", function (thing) {
  4076. if (!option.convert) {
  4077. warn("expected_a_b", thing, "Number(...)", "+");
  4078. }
  4079. const right = thing.expression;
  4080. if (right.id === "(" && right.expression[0].id === "new") {
  4081. warn("unexpected_a_before_b", thing, "+", "new");
  4082. } else if (
  4083. right.constant
  4084. || right.id === "{"
  4085. || (right.id === "[" && right.arity !== "binary")
  4086. ) {
  4087. warn("unexpected_a", thing, "+");
  4088. }
  4089. });
  4090. function delve(the_function) {
  4091. Object.keys(the_function.context).forEach(function (id) {
  4092. if (id !== "ignore") {
  4093. const name = the_function.context[id];
  4094. if (name.parent === the_function) {
  4095. if (
  4096. name.used === 0
  4097. && (
  4098. name.role !== "function"
  4099. || name.parent.arity !== "unary"
  4100. )
  4101. ) {
  4102. warn("unused_a", name);
  4103. } else if (!name.init) {
  4104. warn("uninitialized_a", name);
  4105. }
  4106. }
  4107. }
  4108. });
  4109. }
  4110. function uninitialized_and_unused() {
  4111. // Delve into the functions looking for variables that were not initialized
  4112. // or used. If the file imports or exports, then its global object is also
  4113. // delved.
  4114. if (module_mode === true || option.node) {
  4115. delve(global);
  4116. }
  4117. functions.forEach(delve);
  4118. }
  4119. // Go through the token list, looking at usage of whitespace.
  4120. function whitage() {
  4121. let closer = "(end)";
  4122. let free = false;
  4123. let left = global;
  4124. let margin = 0;
  4125. let nr_comments_skipped = 0;
  4126. let open = true;
  4127. let opening = true;
  4128. let right;
  4129. function pop() {
  4130. const previous = stack.pop();
  4131. closer = previous.closer;
  4132. free = previous.free;
  4133. margin = previous.margin;
  4134. open = previous.open;
  4135. opening = previous.opening;
  4136. }
  4137. function push() {
  4138. stack.push({
  4139. closer,
  4140. free,
  4141. margin,
  4142. open,
  4143. opening
  4144. });
  4145. }
  4146. function expected_at(at) {
  4147. warn(
  4148. "expected_a_at_b_c",
  4149. right,
  4150. artifact(right),
  4151. fudge + at,
  4152. artifact_column(right)
  4153. );
  4154. }
  4155. function at_margin(fit) {
  4156. const at = margin + fit;
  4157. if (right.from !== at) {
  4158. return expected_at(at);
  4159. }
  4160. }
  4161. function no_space_only() {
  4162. if (
  4163. left.id !== "(global)"
  4164. && left.nr + 1 === right.nr
  4165. && (
  4166. left.line !== right.line
  4167. || left.thru !== right.from
  4168. )
  4169. ) {
  4170. warn(
  4171. "unexpected_space_a_b",
  4172. right,
  4173. artifact(left),
  4174. artifact(right)
  4175. );
  4176. }
  4177. }
  4178. function no_space() {
  4179. if (left.line === right.line) {
  4180. if (left.thru !== right.from && nr_comments_skipped === 0) {
  4181. warn(
  4182. "unexpected_space_a_b",
  4183. right,
  4184. artifact(left),
  4185. artifact(right)
  4186. );
  4187. }
  4188. } else {
  4189. if (open) {
  4190. const at = (
  4191. free
  4192. ? margin
  4193. : margin + 8
  4194. );
  4195. if (right.from < at) {
  4196. expected_at(at);
  4197. }
  4198. } else {
  4199. if (right.from !== margin + 8) {
  4200. expected_at(margin + 8);
  4201. }
  4202. }
  4203. }
  4204. }
  4205. function one_space_only() {
  4206. if (left.line !== right.line || left.thru + 1 !== right.from) {
  4207. warn(
  4208. "expected_space_a_b",
  4209. right,
  4210. artifact(left),
  4211. artifact(right)
  4212. );
  4213. }
  4214. }
  4215. function one_space() {
  4216. if (left.line === right.line || !open) {
  4217. if (left.thru + 1 !== right.from && nr_comments_skipped === 0) {
  4218. warn(
  4219. "expected_space_a_b",
  4220. right,
  4221. artifact(left),
  4222. artifact(right)
  4223. );
  4224. }
  4225. } else {
  4226. if (right.from !== margin) {
  4227. expected_at(margin);
  4228. }
  4229. }
  4230. }
  4231. stack = [];
  4232. tokens.forEach(function (the_token) {
  4233. right = the_token;
  4234. if (right.id === "(comment)" || right.id === "(end)") {
  4235. nr_comments_skipped += 1;
  4236. } else {
  4237. // If left is an opener and right is not the closer, then push the previous
  4238. // state. If the token following the opener is on the next line, then this is
  4239. // an open form. If the tokens are on the same line, then it is a closed form.
  4240. // Open form is more readable, with each item (statement, argument, parameter,
  4241. // etc) starting on its own line. Closed form is more compact. Statement blocks
  4242. // are always in open form.
  4243. const new_closer = opener[left.id];
  4244. if (typeof new_closer === "string") {
  4245. if (new_closer !== right.id) {
  4246. opening = left.open || (left.line !== right.line);
  4247. push();
  4248. closer = new_closer;
  4249. if (opening) {
  4250. free = closer === ")" && left.free;
  4251. open = true;
  4252. margin += 4;
  4253. if (right.role === "label") {
  4254. if (right.from !== 0) {
  4255. expected_at(0);
  4256. }
  4257. } else if (right.switch) {
  4258. at_margin(-4);
  4259. } else {
  4260. at_margin(0);
  4261. }
  4262. } else {
  4263. if (right.statement || right.role === "label") {
  4264. warn(
  4265. "expected_line_break_a_b",
  4266. right,
  4267. artifact(left),
  4268. artifact(right)
  4269. );
  4270. }
  4271. free = false;
  4272. open = false;
  4273. no_space_only();
  4274. }
  4275. } else {
  4276. // If left and right are opener and closer, then the placement of right depends
  4277. // on the openness. Illegal pairs (like '{]') have already been detected.
  4278. if (left.line === right.line) {
  4279. no_space();
  4280. } else {
  4281. at_margin(0);
  4282. }
  4283. }
  4284. } else {
  4285. if (right.statement === true) {
  4286. if (left.id === "else") {
  4287. one_space_only();
  4288. } else {
  4289. at_margin(0);
  4290. open = false;
  4291. }
  4292. // If right is a closer, then pop the previous state.
  4293. } else if (right.id === closer) {
  4294. pop();
  4295. if (opening && right.id !== ";") {
  4296. at_margin(0);
  4297. } else {
  4298. no_space_only();
  4299. }
  4300. } else {
  4301. // Left is not an opener, and right is not a closer.
  4302. // The nature of left and right will determine the space between them.
  4303. // If left is ',' or ';' or right is a statement then if open,
  4304. // right must go at the margin, or if closed, a space between.
  4305. if (right.switch) {
  4306. at_margin(-4);
  4307. } else if (right.role === "label") {
  4308. if (right.from !== 0) {
  4309. expected_at(0);
  4310. }
  4311. } else if (left.id === ",") {
  4312. if (!open || (
  4313. (free || closer === "]")
  4314. && left.line === right.line
  4315. )) {
  4316. one_space();
  4317. } else {
  4318. at_margin(0);
  4319. }
  4320. // If right is a ternary operator, line it up on the margin.
  4321. } else if (right.arity === "ternary") {
  4322. if (open) {
  4323. at_margin(0);
  4324. } else {
  4325. warn("use_open", right);
  4326. }
  4327. } else if (
  4328. right.arity === "binary"
  4329. && right.id === "("
  4330. && free
  4331. ) {
  4332. no_space();
  4333. } else if (
  4334. left.id === "."
  4335. || left.id === "?."
  4336. || left.id === "..."
  4337. || right.id === ","
  4338. || right.id === ";"
  4339. || right.id === ":"
  4340. || (
  4341. right.arity === "binary"
  4342. && (right.id === "(" || right.id === "[")
  4343. )
  4344. || (
  4345. right.arity === "function"
  4346. && left.id !== "function"
  4347. )
  4348. ) {
  4349. no_space_only();
  4350. } else if (right.id === "." || right.id === "?.") {
  4351. no_space_only();
  4352. } else if (left.id === ";") {
  4353. if (open) {
  4354. at_margin(0);
  4355. }
  4356. } else if (
  4357. left.arity === "ternary"
  4358. || left.id === "case"
  4359. || left.id === "catch"
  4360. || left.id === "else"
  4361. || left.id === "finally"
  4362. || left.id === "while"
  4363. || right.id === "catch"
  4364. || right.id === "else"
  4365. || right.id === "finally"
  4366. || (right.id === "while" && !right.statement)
  4367. || (left.id === ")" && right.id === "{")
  4368. ) {
  4369. one_space_only();
  4370. } else if (
  4371. left.id === "var"
  4372. || left.id === "const"
  4373. || left.id === "let"
  4374. ) {
  4375. push();
  4376. closer = ";";
  4377. free = false;
  4378. open = left.open;
  4379. if (open) {
  4380. margin = margin + 4;
  4381. at_margin(0);
  4382. } else {
  4383. one_space_only();
  4384. }
  4385. } else if (
  4386. // There is a space between left and right.
  4387. spaceop[left.id] === true
  4388. || spaceop[right.id] === true
  4389. || (
  4390. left.arity === "binary"
  4391. && (left.id === "+" || left.id === "-")
  4392. )
  4393. || (
  4394. right.arity === "binary"
  4395. && (right.id === "+" || right.id === "-")
  4396. )
  4397. || left.id === "function"
  4398. || left.id === ":"
  4399. || (
  4400. (
  4401. left.identifier
  4402. || left.id === "(string)"
  4403. || left.id === "(number)"
  4404. )
  4405. && (
  4406. right.identifier
  4407. || right.id === "(string)"
  4408. || right.id === "(number)"
  4409. )
  4410. )
  4411. || (left.arity === "statement" && right.id !== ";")
  4412. ) {
  4413. one_space();
  4414. } else if (left.arity === "unary" && left.id !== "`") {
  4415. no_space_only();
  4416. }
  4417. }
  4418. }
  4419. nr_comments_skipped = 0;
  4420. delete left.calls;
  4421. delete left.dead;
  4422. delete left.free;
  4423. delete left.init;
  4424. delete left.open;
  4425. delete left.used;
  4426. left = right;
  4427. }
  4428. });
  4429. }
  4430. // The jslint function itself.
  4431. export default Object.freeze(function jslint(
  4432. source = "",
  4433. option_object = empty(),
  4434. global_array = []
  4435. ) {
  4436. try {
  4437. warnings = [];
  4438. option = Object.assign(empty(), option_object);
  4439. anon = "anonymous";
  4440. block_stack = [];
  4441. declared_globals = empty();
  4442. directive_mode = true;
  4443. directives = [];
  4444. early_stop = true;
  4445. exports = empty();
  4446. froms = [];
  4447. fudge = (
  4448. option.fudge
  4449. ? 1
  4450. : 0
  4451. );
  4452. functions = [];
  4453. global = {
  4454. id: "(global)",
  4455. body: true,
  4456. context: empty(),
  4457. from: 0,
  4458. level: 0,
  4459. line: 0,
  4460. live: [],
  4461. loop: 0,
  4462. switch: 0,
  4463. thru: 0
  4464. };
  4465. blockage = global;
  4466. functionage = global;
  4467. json_mode = false;
  4468. mega_mode = false;
  4469. module_mode = false;
  4470. next_token = global;
  4471. property = empty();
  4472. shebang = false;
  4473. stack = [];
  4474. tenure = undefined;
  4475. token = global;
  4476. token_nr = 0;
  4477. var_mode = undefined;
  4478. populate(standard, declared_globals, false);
  4479. populate(global_array, declared_globals, false);
  4480. Object.keys(option).forEach(function (name) {
  4481. if (option[name] === true) {
  4482. const allowed = allowed_option[name];
  4483. if (Array.isArray(allowed)) {
  4484. populate(allowed, declared_globals, false);
  4485. }
  4486. }
  4487. });
  4488. tokenize(source);
  4489. advance();
  4490. if (json_mode) {
  4491. tree = json_value();
  4492. advance("(end)");
  4493. } else {
  4494. // Because browsers encourage combining of script files, the first token might
  4495. // be a semicolon to defend against a missing semicolon in the preceding file.
  4496. if (option.browser) {
  4497. if (next_token.id === ";") {
  4498. advance(";");
  4499. }
  4500. } else {
  4501. // If we are not in a browser, then the file form of strict pragma may be used.
  4502. if (
  4503. next_token.value === "use strict"
  4504. ) {
  4505. advance("(string)");
  4506. advance(";");
  4507. }
  4508. }
  4509. tree = statements();
  4510. advance("(end)");
  4511. functionage = global;
  4512. walk_statement(tree);
  4513. if (warnings.length === 0) {
  4514. uninitialized_and_unused();
  4515. if (!option.white) {
  4516. whitage();
  4517. }
  4518. }
  4519. }
  4520. if (!option.browser) {
  4521. directives.forEach(function (comment) {
  4522. if (comment.directive === "global") {
  4523. warn("missing_browser", comment);
  4524. }
  4525. });
  4526. }
  4527. early_stop = false;
  4528. } catch (e) {
  4529. if (e.name !== "JSLintError") {
  4530. warnings.push(e);
  4531. }
  4532. }
  4533. return {
  4534. directives,
  4535. edition: "2020-03-28",
  4536. exports,
  4537. froms,
  4538. functions,
  4539. global,
  4540. id: "(JSLint)",
  4541. json: json_mode,
  4542. lines,
  4543. module: module_mode === true,
  4544. ok: warnings.length === 0 && !early_stop,
  4545. option,
  4546. property,
  4547. shebang: (
  4548. shebang
  4549. ? lines[0]
  4550. : undefined
  4551. ),
  4552. stop: early_stop,
  4553. tokens,
  4554. tree,
  4555. warnings: warnings.sort(function (a, b) {
  4556. return a.line - b.line || a.column - b.column;
  4557. })
  4558. };
  4559. });