]> code.delx.au - gnu-emacs-elpa/blob - scripts/jslint.js
Set JSLint options to not care.
[gnu-emacs-elpa] / scripts / jslint.js
1 // jslint.js
2 // 2014-07-08
3
4 // Copyright (c) 2002 Douglas Crockford (www.JSLint.com)
5
6 // Permission is hereby granted, free of charge, to any person obtaining a copy
7 // of this software and associated documentation files (the "Software"), to deal
8 // in the Software without restriction, including without limitation the rights
9 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 // copies of the Software, and to permit persons to whom the Software is
11 // furnished to do so, subject to the following conditions:
12
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15
16 // The Software shall be used for Good, not Evil.
17
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
25
26 // WARNING: JSLint will hurt your feelings.
27
28 // JSLINT is a global function. It takes two parameters.
29
30 // var myResult = JSLINT(source, option);
31
32 // The first parameter is either a string or an array of strings. If it is a
33 // string, it will be split on '\n' or '\r'. If it is an array of strings, it
34 // is assumed that each string represents one line. The source can be a
35 // JavaScript text or a JSON text.
36
37 // The second parameter is an optional object of options that control the
38 // operation of JSLINT. Most of the options are booleans: They are all
39 // optional and have a default value of false. One of the options, predef,
40 // can be an array of names, which will be used to declare global variables,
41 // or an object whose keys are used as global names, with a boolean value
42 // that determines if they are assignable.
43
44 // If it checks out, JSLINT returns true. Otherwise, it returns false.
45
46 // If false, you can inspect JSLINT.errors to find out the problems.
47 // JSLINT.errors is an array of objects containing these properties:
48
49 // {
50 // line : The line (relative to 0) at which the lint was found
51 // character : The character (relative to 0) at which the lint was found
52 // reason : The problem
53 // evidence : The text line in which the problem occurred
54 // raw : The raw message before the details were inserted
55 // a : The first detail
56 // b : The second detail
57 // c : The third detail
58 // d : The fourth detail
59 // }
60
61 // If a stopping error was found, a null will be the last element of the
62 // JSLINT.errors array. A stopping error means that JSLint was not confident
63 // enough to continue. It does not necessarily mean that the error was
64 // especially heinous.
65
66 // You can request a data structure that contains JSLint's results.
67
68 // var myData = JSLINT.data();
69
70 // It returns a structure with this form:
71
72 // {
73 // errors: [
74 // {
75 // line: NUMBER,
76 // character: NUMBER,
77 // reason: STRING,
78 // evidence: STRING
79 // }
80 // ],
81 // functions: [
82 // {
83 // name: STRING,
84 // line: NUMBER,
85 // level: NUMBER,
86 // parameter: [
87 // STRING
88 // ],
89 // var: [
90 // STRING
91 // ],
92 // exception: [
93 // STRING
94 // ],
95 // closure: [
96 // STRING
97 // ],
98 // outer: [
99 // STRING
100 // ],
101 // global: [
102 // STRING
103 // ],
104 // label: [
105 // STRING
106 // ]
107 // }
108 // ],
109 // global: [
110 // STRING
111 // ],
112 // member: {
113 // STRING: NUMBER
114 // },
115 // json: BOOLEAN
116 // }
117
118 // You can request a Function Report, which shows all of the functions
119 // and the parameters and vars that they use. This can be used to find
120 // implied global variables and other problems. The report is in HTML and
121 // can be inserted into an HTML <body>. It should be given the result of the
122 // JSLINT.data function.
123
124 // var myReport = JSLINT.report(data);
125
126 // You can request an HTML error report.
127
128 // var myErrorReport = JSLINT.error_report(data);
129
130 // You can obtain an object containing all of the properties found in the
131 // file. JSLINT.property contains an object containing a key for each
132 // property used in the program, the value being the number of times that
133 // property name was used in the file.
134
135 // You can request a properties report, which produces a list of the program's
136 // properties in the form of a /*properties*/ declaration.
137
138 // var myPropertyReport = JSLINT.properties_report(JSLINT.property);
139
140 // You can obtain the parse tree that JSLint constructed while parsing. The
141 // latest tree is kept in JSLINT.tree. A nice stringification can be produced
142 // with
143
144 // JSON.stringify(JSLINT.tree, [
145 // 'string', 'arity', 'name', 'first',
146 // 'second', 'third', 'block', 'else'
147 // ], 4));
148
149 // You can request a context coloring table. It contains information that can be
150 // applied to the file that was analyzed. Context coloring colors functions
151 // based on their nesting level, and variables on the color of the functions
152 // in which they are defined.
153
154 // var myColorization = JSLINT.color(data);
155
156 // It returns an array containing objects of this form:
157
158 // {
159 // from: COLUMN,
160 // thru: COLUMN,
161 // line: ROW,
162 // level: 0 or higher
163 // }
164
165 // JSLint provides three inline directives. They look like slashstar comments,
166 // and allow for setting options, declaring global variables, and establishing a
167 // set of allowed property names.
168
169 // These directives respect function scope.
170
171 // The jslint directive is a special comment that can set one or more options.
172 // For example:
173
174 /*jslint
175 evil: true, nomen: true, regexp: true, todo: true
176 */
177
178 // The current option set is
179
180 // ass true, if assignment expressions should be allowed
181 // bitwise true, if bitwise operators should be allowed
182 // browser true, if the standard browser globals should be predefined
183 // closure true, if Google Closure idioms should be tolerated
184 // continue true, if the continuation statement should be tolerated
185 // debug true, if debugger statements should be allowed
186 // devel true, if logging should be allowed (console, alert, etc.)
187 // eqeq true, if == should be allowed
188 // evil true, if eval should be allowed
189 // forin true, if for in statements need not filter
190 // indent the indentation factor
191 // maxerr the maximum number of errors to allow
192 // maxlen the maximum length of a source line
193 // newcap true, if constructor names capitalization is ignored
194 // node true, if Node.js globals should be predefined
195 // nomen true, if names may have dangling _
196 // passfail true, if the scan should stop on first error
197 // plusplus true, if increment/decrement should be allowed
198 // properties true, if all property names must be declared with /*properties*/
199 // regexp true, if the . should be allowed in regexp literals
200 // rhino true, if the Rhino environment globals should be predefined
201 // unparam true, if unused parameters should be tolerated
202 // sloppy true, if the 'use strict'; pragma is optional
203 // stupid true, if really stupid practices are tolerated
204 // sub true, if all forms of subscript notation are tolerated
205 // todo true, if TODO comments are tolerated
206 // vars true, if multiple var statements per function should be allowed
207 // white true, if sloppy whitespace is tolerated
208
209 // The properties directive declares an exclusive list of property names.
210 // Any properties named in the program that are not in the list will
211 // produce a warning.
212
213 // For example:
214
215 /*properties
216 '\b', '\t', '\n', '\f', '\r', '!', '!=', '!==', '"', '%', '\'', '(begin)',
217 '(error)', '*', '+', '-', '/', '<', '<=', '==', '===', '>', '>=', '\\', a,
218 a_label, a_scope, already_defined, and, apply, arguments, arity, ass,
219 assign, assignment_expression, assignment_function_expression, at, avoid_a,
220 b, bad_assignment, bad_constructor, bad_in_a, bad_invocation, bad_new,
221 bad_number, bad_operand, bad_wrap, bitwise, block, break, breakage, browser,
222 c, call, charAt, charCodeAt, character, closure, code, color, combine_var,
223 comments, conditional_assignment, confusing_a, confusing_regexp,
224 constructor_name_a, continue, control_a, couch, create, d, dangling_a, data,
225 dead, debug, deleted, devel, disrupt, duplicate_a, edge, edition, elif,
226 else, empty_block, empty_case, empty_class, entityify, eqeq, error_report,
227 errors, evidence, evil, exception, exec, expected_a_at_b_c, expected_a_b,
228 expected_a_b_from_c_d, expected_id_a, expected_identifier_a,
229 expected_identifier_a_reserved, expected_number_a, expected_operator_a,
230 expected_positive_a, expected_small_a, expected_space_a_b,
231 expected_string_a, exports, f, first, flag, floor, forEach, for_if, forin,
232 from, fromCharCode, fud, function, function_block, function_eval,
233 function_loop, function_statement, function_strict, functions, global,
234 hasOwnProperty, id, identifier, identifier_function, immed, implied_evil,
235 indent, indexOf, infix_in, init, insecure_a, isAlpha, isArray, isDigit,
236 isNaN, join, jslint, json, keys, kind, label, labeled, lbp,
237 leading_decimal_a, led, left, length, level, line, loopage, master, match,
238 maxerr, maxlen, message, missing_a, missing_a_after_b, missing_property,
239 missing_space_a_b, missing_use_strict, mode, move_invocation, move_var, n,
240 name, name_function, nested_comment, newcap, node, nomen, not,
241 not_a_constructor, not_a_defined, not_a_function, not_a_label, not_a_scope,
242 not_greater, nud, number, octal_a, open, outer, parameter,
243 parameter_a_get_b, parameter_arguments_a, parameter_set_a, params, paren,
244 passfail, plusplus, pop, postscript, predef, properties, properties_report,
245 property, prototype, push, quote, r, radix, raw, read_only, reason,
246 redefinition_a_b, regexp, relation, replace, report, reserved, reserved_a,
247 rhino, right, scanned_a_b, scope, search, second, shift, slash_equal, slice,
248 sloppy, sort, split, statement, statement_block, stop, stopping,
249 strange_loop, strict, string, stupid, sub, subscript, substr, supplant,
250 sync_a, t, tag_a_in_b, test, third, thru, toString, todo, todo_comment,
251 token, tokens, too_long, too_many, trailing_decimal_a, tree, unclosed,
252 unclosed_comment, unclosed_regexp, unescaped_a, unexpected_a,
253 unexpected_char_a, unexpected_comment, unexpected_label_a,
254 unexpected_property_a, unexpected_space_a_b, unexpected_typeof_a,
255 uninitialized_a, unnecessary_else, unnecessary_initialize, unnecessary_use,
256 unparam, unreachable_a_b, unsafe, unused_a, url, use_array, use_braces,
257 use_nested_if, use_object, use_or, use_param, use_spaces, used,
258 used_before_a, var, var_a_not, var_loop, vars, varstatement, warn, warning,
259 was, weird_assignment, weird_condition, weird_new, weird_program,
260 weird_relation, weird_ternary, white, wrap, wrap_immediate, wrap_regexp,
261 write_is_wrong, writeable
262 */
263
264 // The global directive is used to declare global variables that can
265 // be accessed by the program. If a declaration is true, then the variable
266 // is writeable. Otherwise, it is read-only.
267
268 // We build the application inside a function so that we produce only a single
269 // global variable. That function will be invoked immediately, and its return
270 // value is the JSLINT function itself. That function is also an object that
271 // can contain data and other functions.
272
273 module.exports = (function () {
274 'use strict';
275
276 function array_to_object(array, value) {
277
278 // Make an object from an array of keys and a common value.
279
280 var i, length = array.length, object = Object.create(null);
281 for (i = 0; i < length; i += 1) {
282 object[array[i]] = value;
283 }
284 return object;
285 }
286
287
288 var allowed_option = {
289 ass : true,
290 bitwise : true,
291 browser : true,
292 closure : true,
293 continue : true,
294 couch : true,
295 debug : true,
296 devel : true,
297 eqeq : true,
298 evil : true,
299 forin : true,
300 indent : 10,
301 maxerr : 1000,
302 maxlen : 256,
303 newcap : true,
304 node : true,
305 nomen : true,
306 passfail : true,
307 plusplus : true,
308 properties: true,
309 regexp : true,
310 rhino : true,
311 unparam : true,
312 sloppy : true,
313 stupid : true,
314 sub : true,
315 todo : true,
316 vars : true,
317 white : true
318 },
319 anonname, // The guessed name for anonymous functions.
320
321 // These are operators that should not be used with the ! operator.
322
323 bang = {
324 '<' : true,
325 '<=' : true,
326 '==' : true,
327 '===': true,
328 '!==': true,
329 '!=' : true,
330 '>' : true,
331 '>=' : true,
332 '+' : true,
333 '-' : true,
334 '*' : true,
335 '/' : true,
336 '%' : true
337 },
338 begin, // The root token
339 block_var, // vars defined in the current block
340
341 // browser contains a set of global names that are commonly provided by a
342 // web browser environment.
343
344 browser = array_to_object([
345 'clearInterval', 'clearTimeout', 'document', 'event', 'FormData',
346 'frames', 'history', 'Image', 'localStorage', 'location', 'name',
347 'navigator', 'Option', 'parent', 'screen', 'sessionStorage',
348 'setInterval', 'setTimeout', 'Storage', 'window', 'XMLHttpRequest'
349 ], false),
350
351 // bundle contains the text messages.
352
353 bundle = {
354 a_label: "'{a}' is a statement label.",
355 a_scope: "'{a}' used out of scope.",
356 already_defined: "'{a}' is already defined.",
357 and: "The '&&' subexpression should be wrapped in parens.",
358 assignment_expression: "Unexpected assignment expression.",
359 assignment_function_expression: "Expected an assignment or " +
360 "function call and instead saw an expression.",
361 avoid_a: "Avoid '{a}'.",
362 bad_assignment: "Bad assignment.",
363 bad_constructor: "Bad constructor.",
364 bad_in_a: "Bad for in variable '{a}'.",
365 bad_invocation: "Bad invocation.",
366 bad_new: "Do not use 'new' for side effects.",
367 bad_number: "Bad number '{a}'.",
368 bad_operand: "Bad operand.",
369 bad_wrap: "Do not wrap function literals in parens unless they " +
370 "are to be immediately invoked.",
371 combine_var: "Combine this with the previous 'var' statement.",
372 conditional_assignment: "Expected a conditional expression and " +
373 "instead saw an assignment.",
374 confusing_a: "Confusing use of '{a}'.",
375 confusing_regexp: "Confusing regular expression.",
376 constructor_name_a: "A constructor name '{a}' should start with " +
377 "an uppercase letter.",
378 control_a: "Unexpected control character '{a}'.",
379 dangling_a: "Unexpected dangling '_' in '{a}'.",
380 deleted: "Only properties should be deleted.",
381 duplicate_a: "Duplicate '{a}'.",
382 empty_block: "Empty block.",
383 empty_case: "Empty case.",
384 empty_class: "Empty class.",
385 evil: "eval is evil.",
386 expected_a_b: "Expected '{a}' and instead saw '{b}'.",
387 expected_a_b_from_c_d: "Expected '{a}' to match '{b}' from line " +
388 "{c} and instead saw '{d}'.",
389 expected_a_at_b_c: "Expected '{a}' at column {b}, not column {c}.",
390 expected_id_a: "Expected an id, and instead saw #{a}.",
391 expected_identifier_a: "Expected an identifier and instead saw '{a}'.",
392 expected_identifier_a_reserved: "Expected an identifier and " +
393 "instead saw '{a}' (a reserved word).",
394 expected_number_a: "Expected a number and instead saw '{a}'.",
395 expected_operator_a: "Expected an operator and instead saw '{a}'.",
396 expected_positive_a: "Expected a positive number and instead saw '{a}'",
397 expected_small_a: "Expected a small positive integer and instead saw '{a}'",
398 expected_space_a_b: "Expected exactly one space between '{a}' and '{b}'.",
399 expected_string_a: "Expected a string and instead saw '{a}'.",
400 for_if: "The body of a for in should be wrapped in an if " +
401 "statement to filter unwanted properties from the prototype.",
402 function_block: "Function statements should not be placed in blocks." +
403 "Use a function expression or move the statement to the top of " +
404 "the outer function.",
405 function_eval: "The Function constructor is eval.",
406 function_loop: "Don't make functions within a loop.",
407 function_statement: "Function statements are not invocable. " +
408 "Wrap the whole function invocation in parens.",
409 function_strict: "Use the function form of 'use strict'.",
410 identifier_function: "Expected an identifier in an assignment " +
411 "and instead saw a function invocation.",
412 implied_evil: "Implied eval is evil. Pass a function instead of a string.",
413 infix_in: "Unexpected 'in'. Compare with undefined, or use the " +
414 "hasOwnProperty method instead.",
415 insecure_a: "Insecure '{a}'.",
416 isNaN: "Use the isNaN function to compare with NaN.",
417 leading_decimal_a: "A leading decimal point can be confused with a dot: '.{a}'.",
418 missing_a: "Missing '{a}'.",
419 missing_a_after_b: "Missing '{a}' after '{b}'.",
420 missing_property: "Missing property name.",
421 missing_space_a_b: "Missing space between '{a}' and '{b}'.",
422 missing_use_strict: "Missing 'use strict' statement.",
423 move_invocation: "Move the invocation into the parens that " +
424 "contain the function.",
425 move_var: "Move 'var' declarations to the top of the function.",
426 name_function: "Missing name in function statement.",
427 nested_comment: "Nested comment.",
428 not: "Nested not.",
429 not_a_constructor: "Do not use {a} as a constructor.",
430 not_a_defined: "'{a}' has not been fully defined yet.",
431 not_a_function: "'{a}' is not a function.",
432 not_a_label: "'{a}' is not a label.",
433 not_a_scope: "'{a}' is out of scope.",
434 not_greater: "'{a}' should not be greater than '{b}'.",
435 octal_a: "Don't use octal: '{a}'. Use '\\u....' instead.",
436 parameter_arguments_a: "Do not mutate parameter '{a}' when using 'arguments'.",
437 parameter_a_get_b: "Unexpected parameter '{a}' in get {b} function.",
438 parameter_set_a: "Expected parameter (value) in set {a} function.",
439 radix: "Missing radix parameter.",
440 read_only: "Read only.",
441 redefinition_a_b: "Redefinition of '{a}' from line {b}.",
442 reserved_a: "Reserved name '{a}'.",
443 scanned_a_b: "{a} ({b}% scanned).",
444 slash_equal: "A regular expression literal can be confused with '/='.",
445 statement_block: "Expected to see a statement and instead saw a block.",
446 stopping: "Stopping.",
447 strange_loop: "Strange loop.",
448 strict: "Strict violation.",
449 subscript: "['{a}'] is better written in dot notation.",
450 sync_a: "Unexpected sync method: '{a}'.",
451 tag_a_in_b: "A '<{a}>' must be within '<{b}>'.",
452 todo_comment: "Unexpected TODO comment.",
453 too_long: "Line too long.",
454 too_many: "Too many errors.",
455 trailing_decimal_a: "A trailing decimal point can be confused " +
456 "with a dot: '.{a}'.",
457 unclosed: "Unclosed string.",
458 unclosed_comment: "Unclosed comment.",
459 unclosed_regexp: "Unclosed regular expression.",
460 unescaped_a: "Unescaped '{a}'.",
461 unexpected_a: "Unexpected '{a}'.",
462 unexpected_char_a: "Unexpected character '{a}'.",
463 unexpected_comment: "Unexpected comment.",
464 unexpected_label_a: "Unexpected label '{a}'.",
465 unexpected_property_a: "Unexpected /*property*/ '{a}'.",
466 unexpected_space_a_b: "Unexpected space between '{a}' and '{b}'.",
467 unexpected_typeof_a: "Unexpected 'typeof'. " +
468 "Use '===' to compare directly with {a}.",
469 uninitialized_a: "Uninitialized '{a}'.",
470 unnecessary_else: "Unnecessary 'else' after disruption.",
471 unnecessary_initialize: "It is not necessary to initialize '{a}' " +
472 "to 'undefined'.",
473 unnecessary_use: "Unnecessary 'use strict'.",
474 unreachable_a_b: "Unreachable '{a}' after '{b}'.",
475 unsafe: "Unsafe character.",
476 unused_a: "Unused '{a}'.",
477 url: "JavaScript URL.",
478 use_array: "Use the array literal notation [].",
479 use_braces: "Spaces are hard to count. Use {{a}}.",
480 use_nested_if: "Expected 'else { if' and instead saw 'else if'.",
481 use_object: "Use the object literal notation {} or Object.create(null).",
482 use_or: "Use the || operator.",
483 use_param: "Use a named parameter.",
484 use_spaces: "Use spaces, not tabs.",
485 used_before_a: "'{a}' was used before it was defined.",
486 var_a_not: "Variable {a} was not declared correctly.",
487 var_loop: "Don't declare variables in a loop.",
488 weird_assignment: "Weird assignment.",
489 weird_condition: "Weird condition.",
490 weird_new: "Weird construction. Delete 'new'.",
491 weird_program: "Weird program.",
492 weird_relation: "Weird relation.",
493 weird_ternary: "Weird ternary.",
494 wrap_immediate: "Wrap an immediate function invocation in " +
495 "parentheses to assist the reader in understanding that the " +
496 "expression is the result of a function, and not the " +
497 "function itself.",
498 wrap_regexp: "Wrap the /regexp/ literal in parens to " +
499 "disambiguate the slash operator.",
500 write_is_wrong: "document.write can be a form of eval."
501 },
502 closure = array_to_object([
503 'goog'
504 ], false),
505 comments,
506 comments_off,
507 couch = array_to_object([
508 'emit', 'getRow', 'isArray', 'log', 'provides', 'registerType',
509 'require', 'send', 'start', 'sum', 'toJSON'
510 ], false),
511
512 descapes = {
513 'b': '\b',
514 't': '\t',
515 'n': '\n',
516 'f': '\f',
517 'r': '\r',
518 '"': '"',
519 '/': '/',
520 '\\': '\\',
521 '!': '!'
522 },
523
524 devel = array_to_object([
525 'alert', 'confirm', 'console', 'Debug', 'opera', 'prompt', 'WSH'
526 ], false),
527 directive,
528 escapes = {
529 '\b': '\\b',
530 '\t': '\\t',
531 '\n': '\\n',
532 '\f': '\\f',
533 '\r': '\\r',
534 '\'': '\\\'',
535 '"' : '\\"',
536 '/' : '\\/',
537 '\\': '\\\\'
538 },
539
540 funct, // The current function
541
542 functions, // All of the functions
543 global_funct, // The global body
544 global_scope, // The global scope
545 in_block, // Where function statements are not allowed
546 indent,
547 itself, // JSLINT itself
548 json_mode,
549 lex, // the tokenizer
550 lines,
551 lookahead,
552 node = array_to_object([
553 'Buffer', 'clearImmediate', 'clearInterval', 'clearTimeout',
554 'console', 'exports', 'global', 'module', 'process',
555 'require', 'setImmediate', 'setInterval', 'setTimeout',
556 '__dirname', '__filename'
557 ], false),
558 node_js,
559 numbery = array_to_object(['indexOf', 'lastIndexOf', 'search'], true),
560 next_token,
561 option,
562 predefined, // Global variables defined by option
563 prereg,
564 prev_token,
565 property,
566 protosymbol,
567 regexp_flag = array_to_object(['g', 'i', 'm'], true),
568 return_this = function return_this() {
569 return this;
570 },
571 rhino = array_to_object([
572 'defineClass', 'deserialize', 'gc', 'help', 'load', 'loadClass',
573 'print', 'quit', 'readFile', 'readUrl', 'runCommand', 'seal',
574 'serialize', 'spawn', 'sync', 'toint32', 'version'
575 ], false),
576
577 scope, // An object containing an object for each variable in scope
578 semicolon_coda = array_to_object([';', '"', '\'', ')'], true),
579
580 // standard contains the global names that are provided by the
581 // ECMAScript standard.
582
583 standard = array_to_object([
584 'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent',
585 'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError',
586 'Function', 'isFinite', 'isNaN', 'JSON', 'Map', 'Math', 'Number',
587 'Object', 'parseInt', 'parseFloat', 'Promise', 'Proxy',
588 'RangeError', 'ReferenceError', 'Reflect', 'RegExp', 'Set',
589 'String', 'Symbol', 'SyntaxError', 'System', 'TypeError',
590 'URIError', 'WeakMap', 'WeakSet'
591 ], false),
592
593 strict_mode,
594 syntax = Object.create(null),
595 token,
596 tokens,
597 var_mode,
598 warnings,
599
600 // Regular expressions. Some of these are stupidly long.
601
602 // carriage return, carriage return linefeed, or linefeed
603 crlfx = /\r\n?|\n/,
604 // unsafe characters that are silently deleted by one or more browsers
605 cx = /[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/,
606 // identifier
607 ix = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/,
608 // javascript url
609 jx = /^(?:javascript|jscript|ecmascript|vbscript)\s*:/i,
610 // star slash
611 lx = /\*\/|\/\*/,
612 // characters in strings that need escapement
613 nx = /[\u0000-\u001f'\\\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
614 // sync
615 syx = /Sync$/,
616 // comment todo
617 tox = /^\W*to\s*do(?:\W|$)/i,
618 // token
619 tx = /^\s*([(){}\[\]\?.,:;'"~#@`]|={1,3}|\/(\*(jslint|properties|property|members?|globals?)?|=|\/)?|\*[\/=]?|\+(?:=|\++)?|-(?:=|-+)?|[\^%]=?|&[&=]?|\|[|=]?|>{1,3}=?|<(?:[\/=!]|\!(\[|--)?|<=?)?|\!(\!|==?)?|[a-zA-Z_$][a-zA-Z0-9_$]*|[0-9]+(?:[xX][0-9a-fA-F]+|\.[0-9]*)?(?:[eE][+\-]?[0-9]+)?)/;
620
621
622 if (typeof String.prototype.entityify !== 'function') {
623 String.prototype.entityify = function () {
624 return this
625 .replace(/&/g, '&amp;')
626 .replace(/</g, '&lt;')
627 .replace(/>/g, '&gt;');
628 };
629 }
630
631 if (typeof String.prototype.isAlpha !== 'function') {
632 String.prototype.isAlpha = function () {
633 return (this >= 'a' && this <= 'z\uffff') ||
634 (this >= 'A' && this <= 'Z\uffff');
635 };
636 }
637
638 if (typeof String.prototype.isDigit !== 'function') {
639 String.prototype.isDigit = function () {
640 return (this >= '0' && this <= '9');
641 };
642 }
643
644 if (typeof String.prototype.supplant !== 'function') {
645 String.prototype.supplant = function (o) {
646 return this.replace(/\{([^{}]*)\}/g, function (a, b) {
647 var replacement = o[b];
648 return typeof replacement === 'string' ||
649 typeof replacement === 'number' ? replacement : a;
650 });
651 };
652 }
653
654
655 function sanitize(a) {
656
657 // Escapify a troublesome character.
658
659 return escapes[a] ||
660 '\\u' + ('0000' + a.charCodeAt().toString(16)).slice(-4);
661 }
662
663
664 function add_to_predefined(group) {
665 Object.keys(group).forEach(function (name) {
666 predefined[name] = group[name];
667 });
668 }
669
670
671 function assume() {
672 if (option.browser) {
673 add_to_predefined(browser);
674 option.browser = false;
675 }
676 if (option.closure) {
677 add_to_predefined(closure);
678 }
679 if (option.couch) {
680 add_to_predefined(couch);
681 option.couch = false;
682 }
683 if (option.devel) {
684 add_to_predefined(devel);
685 option.devel = false;
686 }
687 if (option.node) {
688 add_to_predefined(node);
689 option.node = false;
690 node_js = true;
691 }
692 if (option.rhino) {
693 add_to_predefined(rhino);
694 option.rhino = false;
695 }
696 }
697
698
699 // Produce an error warning.
700
701 function artifact(tok) {
702 if (!tok) {
703 tok = next_token;
704 }
705 return tok.id === '(number)' ? tok.number : tok.string;
706 }
707
708 function quit(message, line, character) {
709 throw {
710 name: 'JSLintError',
711 line: line,
712 character: character,
713 message: bundle.scanned_a_b.supplant({
714 a: bundle[message] || message,
715 b: Math.floor((line / lines.length) * 100)
716 })
717 };
718 }
719
720 function warn(code, line, character, a, b, c, d) {
721 var warning = { // ~~
722 id: '(error)',
723 raw: bundle[code] || code,
724 code: code,
725 evidence: lines[line - 1] || '',
726 line: line,
727 character: character,
728 a: a || artifact(this),
729 b: b,
730 c: c,
731 d: d
732 };
733 warning.reason = warning.raw.supplant(warning);
734 itself.errors.push(warning);
735 if (option.passfail) {
736 quit('stopping', line, character);
737 }
738 warnings += 1;
739 if (warnings >= option.maxerr) {
740 quit('too_many', line, character);
741 }
742 return warning;
743 }
744
745 function stop(code, line, character, a, b, c, d) {
746 var warning = warn(code, line, character, a, b, c, d);
747 quit('stopping', warning.line, warning.character);
748 }
749
750 function expected_at(at) {
751 if (!option.white && next_token.from !== at) {
752 next_token.warn('expected_a_at_b_c', '', at, next_token.from);
753 }
754 }
755
756 // lexical analysis and token construction
757
758 lex = (function lex() {
759 var character, c, from, length, line, pos, source_row;
760
761 // Private lex methods
762
763 function next_line() {
764 var at;
765 character = 1;
766 source_row = lines[line];
767 line += 1;
768 if (source_row === undefined) {
769 return false;
770 }
771 at = source_row.search(/\t/);
772 if (at >= 0) {
773 if (option.white) {
774 source_row = source_row.replace(/\t/g, ' ');
775 } else {
776 warn('use_spaces', line, at + 1);
777 }
778 }
779 at = source_row.search(cx);
780 if (at >= 0) {
781 warn('unsafe', line, at);
782 }
783 if (option.maxlen && option.maxlen < source_row.length) {
784 warn('too_long', line, source_row.length);
785 }
786 return true;
787 }
788
789 // Produce a token object. The token inherits from a syntax symbol.
790
791 function it(type, value) {
792 var id, the_token;
793 if (type === '(string)') {
794 if (jx.test(value)) {
795 warn('url', line, from);
796 }
797 }
798 the_token = Object.create(syntax[(
799 type === '(punctuator)' || (type === '(identifier)' &&
800 Object.prototype.hasOwnProperty.call(syntax, value))
801 ? value
802 : type
803 )] || syntax['(error)']);
804 if (type === '(identifier)') {
805 the_token.identifier = true;
806 if (value === '__iterator__' || value === '__proto__') {
807 stop('reserved_a', line, from, value);
808 } else if (!option.nomen &&
809 (value.charAt(0) === '_' ||
810 value.charAt(value.length - 1) === '_')) {
811 warn('dangling_a', line, from, value);
812 }
813 }
814 if (type === '(number)') {
815 the_token.number = +value;
816 } else if (value !== undefined) {
817 the_token.string = String(value);
818 }
819 the_token.line = line;
820 the_token.from = from;
821 the_token.thru = character;
822 if (comments.length) {
823 the_token.comments = comments;
824 comments = [];
825 }
826 id = the_token.id;
827 prereg = id && (
828 ('(,=:[!&|?{};~+-*%^<>'.indexOf(id.charAt(id.length - 1)) >= 0) ||
829 id === 'return' || id === 'case'
830 );
831 return the_token;
832 }
833
834 function match(x) {
835 var exec = x.exec(source_row), first;
836 if (exec) {
837 length = exec[0].length;
838 first = exec[1];
839 c = first.charAt(0);
840 source_row = source_row.slice(length);
841 from = character + length - first.length;
842 character += length;
843 return first;
844 }
845 for (;;) {
846 if (!source_row) {
847 if (!option.white) {
848 warn('unexpected_char_a', line, character - 1, '(space)');
849 }
850 return;
851 }
852 c = source_row.charAt(0);
853 if (c !== ' ') {
854 break;
855 }
856 source_row = source_row.slice(1);
857 character += 1;
858 }
859 stop('unexpected_char_a', line, character, c);
860
861 }
862
863 function string(x) {
864 var ch, at = 0, r = '', result;
865
866 function hex(n) {
867 var i = parseInt(source_row.substr(at + 1, n), 16);
868 at += n;
869 if (i >= 32 && i <= 126 &&
870 i !== 34 && i !== 92 && i !== 39) {
871 warn('unexpected_a', line, character, '\\');
872 }
873 character += n;
874 ch = String.fromCharCode(i);
875 }
876
877 if (json_mode && x !== '"') {
878 warn('expected_a_b', line, character, '"', x);
879 }
880
881 for (;;) {
882 while (at >= source_row.length) {
883 at = 0;
884 if (!next_line()) {
885 stop('unclosed', line - 1, from);
886 }
887 }
888 ch = source_row.charAt(at);
889 if (ch === x) {
890 character += 1;
891 source_row = source_row.slice(at + 1);
892 result = it('(string)', r);
893 result.quote = x;
894 return result;
895 }
896 if (ch < ' ') {
897 if (ch === '\n' || ch === '\r') {
898 break;
899 }
900 warn('control_a', line, character + at,
901 source_row.slice(0, at));
902 } else if (ch === '\\') {
903 at += 1;
904 character += 1;
905 ch = source_row.charAt(at);
906 switch (ch) {
907 case '':
908 warn('unexpected_a', line, character, '\\');
909 next_line();
910 at = -1;
911 break;
912 case '\'':
913 if (json_mode) {
914 warn('unexpected_a', line, character, '\\\'');
915 }
916 break;
917 case 'u':
918 hex(4);
919 break;
920 case 'v':
921 if (json_mode) {
922 warn('unexpected_a', line, character, '\\v');
923 }
924 ch = '\v';
925 break;
926 case 'x':
927 if (json_mode) {
928 warn('unexpected_a', line, character, '\\x');
929 }
930 hex(2);
931 break;
932 default:
933 if (typeof descapes[ch] !== 'string') {
934 warn(ch >= '0' && ch <= '7' ? 'octal_a' : 'unexpected_a',
935 line, character, '\\' + ch);
936 } else {
937 ch = descapes[ch];
938 }
939 }
940 }
941 r += ch;
942 character += 1;
943 at += 1;
944 }
945 }
946
947 function number(snippet) {
948 var digit;
949 if (source_row.charAt(0).isAlpha()) {
950 warn('expected_space_a_b',
951 line, character, c, source_row.charAt(0));
952 }
953 if (c === '0') {
954 digit = snippet.charAt(1);
955 if (digit.isDigit()) {
956 if (token.id !== '.') {
957 warn('unexpected_a', line, character, snippet);
958 }
959 } else if (json_mode && (digit === 'x' || digit === 'X')) {
960 warn('unexpected_a', line, character, '0x');
961 }
962 }
963 if (snippet.slice(snippet.length - 1) === '.') {
964 warn('trailing_decimal_a', line, character, snippet);
965 }
966 digit = +snippet;
967 if (!isFinite(digit)) {
968 warn('bad_number', line, character, snippet);
969 }
970 snippet = digit;
971 return it('(number)', snippet);
972 }
973
974 function comment(snippet, type) {
975 if (comments_off) {
976 warn('unexpected_comment', line, character);
977 } else if (!option.todo && tox.test(snippet)) {
978 warn('todo_comment', line, character);
979 }
980 comments.push({
981 id: type,
982 from: from,
983 thru: character,
984 line: line,
985 string: snippet
986 });
987 }
988
989 function regexp() {
990 var at = 0,
991 b,
992 bit,
993 depth = 0,
994 flag = '',
995 high,
996 letter,
997 low,
998 potential,
999 quote,
1000 result;
1001 for (;;) {
1002 b = true;
1003 c = source_row.charAt(at);
1004 at += 1;
1005 switch (c) {
1006 case '':
1007 stop('unclosed_regexp', line, from);
1008 return;
1009 case '/':
1010 if (depth > 0) {
1011 warn('unescaped_a', line, from + at, '/');
1012 }
1013 c = source_row.slice(0, at - 1);
1014 potential = Object.create(regexp_flag);
1015 for (;;) {
1016 letter = source_row.charAt(at);
1017 if (potential[letter] !== true) {
1018 break;
1019 }
1020 potential[letter] = false;
1021 at += 1;
1022 flag += letter;
1023 }
1024 if (source_row.charAt(at).isAlpha()) {
1025 stop('unexpected_a', line, from, source_row.charAt(at));
1026 }
1027 character += at;
1028 source_row = source_row.slice(at);
1029 quote = source_row.charAt(0);
1030 if (quote === '/' || quote === '*') {
1031 stop('confusing_regexp', line, from);
1032 }
1033 result = it('(regexp)', c);
1034 result.flag = flag;
1035 return result;
1036 case '\\':
1037 c = source_row.charAt(at);
1038 if (c < ' ') {
1039 warn('control_a', line, from + at, String(c));
1040 } else if (c === '<') {
1041 warn('unexpected_a', line, from + at, '\\');
1042 }
1043 at += 1;
1044 break;
1045 case '(':
1046 depth += 1;
1047 b = false;
1048 if (source_row.charAt(at) === '?') {
1049 at += 1;
1050 switch (source_row.charAt(at)) {
1051 case ':':
1052 case '=':
1053 case '!':
1054 at += 1;
1055 break;
1056 default:
1057 warn('expected_a_b', line, from + at,
1058 ':', source_row.charAt(at));
1059 }
1060 }
1061 break;
1062 case '|':
1063 b = false;
1064 break;
1065 case ')':
1066 if (depth === 0) {
1067 warn('unescaped_a', line, from + at, ')');
1068 } else {
1069 depth -= 1;
1070 }
1071 break;
1072 case ' ':
1073 pos = 1;
1074 while (source_row.charAt(at) === ' ') {
1075 at += 1;
1076 pos += 1;
1077 }
1078 if (pos > 1) {
1079 warn('use_braces', line, from + at, pos);
1080 }
1081 break;
1082 case '[':
1083 c = source_row.charAt(at);
1084 if (c === '^') {
1085 at += 1;
1086 if (!option.regexp) {
1087 warn('insecure_a', line, from + at, c);
1088 } else if (source_row.charAt(at) === ']') {
1089 stop('unescaped_a', line, from + at, '^');
1090 }
1091 }
1092 bit = false;
1093 if (c === ']') {
1094 warn('empty_class', line, from + at - 1);
1095 bit = true;
1096 }
1097 klass: do {
1098 c = source_row.charAt(at);
1099 at += 1;
1100 switch (c) {
1101 case '[':
1102 case '^':
1103 warn('unescaped_a', line, from + at, c);
1104 bit = true;
1105 break;
1106 case '-':
1107 if (bit) {
1108 bit = false;
1109 } else {
1110 warn('unescaped_a', line, from + at, '-');
1111 bit = true;
1112 }
1113 break;
1114 case ']':
1115 if (!bit) {
1116 warn('unescaped_a', line, from + at - 1, '-');
1117 }
1118 break klass;
1119 case '\\':
1120 c = source_row.charAt(at);
1121 if (c < ' ') {
1122 warn('control_a', line, from + at, String(c));
1123 } else if (c === '<') {
1124 warn('unexpected_a', line, from + at, '\\');
1125 }
1126 at += 1;
1127 bit = true;
1128 break;
1129 case '/':
1130 warn('unescaped_a', line, from + at - 1, '/');
1131 bit = true;
1132 break;
1133 default:
1134 bit = true;
1135 }
1136 } while (c);
1137 break;
1138 case '.':
1139 if (!option.regexp) {
1140 warn('insecure_a', line, from + at, c);
1141 }
1142 break;
1143 case ']':
1144 case '?':
1145 case '{':
1146 case '}':
1147 case '+':
1148 case '*':
1149 warn('unescaped_a', line, from + at, c);
1150 break;
1151 }
1152 if (b) {
1153 switch (source_row.charAt(at)) {
1154 case '?':
1155 case '+':
1156 case '*':
1157 at += 1;
1158 if (source_row.charAt(at) === '?') {
1159 at += 1;
1160 }
1161 break;
1162 case '{':
1163 at += 1;
1164 c = source_row.charAt(at);
1165 if (c < '0' || c > '9') {
1166 warn('expected_number_a', line,
1167 from + at, c);
1168 }
1169 at += 1;
1170 low = +c;
1171 for (;;) {
1172 c = source_row.charAt(at);
1173 if (c < '0' || c > '9') {
1174 break;
1175 }
1176 at += 1;
1177 low = +c + (low * 10);
1178 }
1179 high = low;
1180 if (c === ',') {
1181 at += 1;
1182 high = Infinity;
1183 c = source_row.charAt(at);
1184 if (c >= '0' && c <= '9') {
1185 at += 1;
1186 high = +c;
1187 for (;;) {
1188 c = source_row.charAt(at);
1189 if (c < '0' || c > '9') {
1190 break;
1191 }
1192 at += 1;
1193 high = +c + (high * 10);
1194 }
1195 }
1196 }
1197 if (source_row.charAt(at) !== '}') {
1198 warn('expected_a_b', line, from + at,
1199 '}', c);
1200 } else {
1201 at += 1;
1202 }
1203 if (source_row.charAt(at) === '?') {
1204 at += 1;
1205 }
1206 if (low > high) {
1207 warn('not_greater', line, from + at,
1208 low, high);
1209 }
1210 break;
1211 }
1212 }
1213 }
1214 c = source_row.slice(0, at - 1);
1215 character += at;
1216 source_row = source_row.slice(at);
1217 return it('(regexp)', c);
1218 }
1219
1220 // Public lex methods
1221
1222 return {
1223 init: function (source) {
1224 if (typeof source === 'string') {
1225 lines = source.split(crlfx);
1226 } else {
1227 lines = source;
1228 }
1229 line = 0;
1230 next_line();
1231 from = 1;
1232 },
1233
1234 // token -- this is called by advance to get the next token.
1235
1236 token: function () {
1237 var first, i, snippet;
1238
1239 for (;;) {
1240 while (!source_row) {
1241 if (!next_line()) {
1242 return it('(end)');
1243 }
1244 }
1245 snippet = match(tx);
1246 if (snippet) {
1247
1248 // identifier
1249
1250 first = snippet.charAt(0);
1251 if (first.isAlpha() || first === '_' || first === '$') {
1252 return it('(identifier)', snippet);
1253 }
1254
1255 // number
1256
1257 if (first.isDigit()) {
1258 return number(snippet);
1259 }
1260 switch (snippet) {
1261
1262 // string
1263
1264 case '"':
1265 case "'":
1266 return string(snippet);
1267
1268 // // comment
1269
1270 case '//':
1271 comment(source_row, '//');
1272 source_row = '';
1273 break;
1274
1275 // /* comment
1276
1277 case '/*':
1278 for (;;) {
1279 i = source_row.search(lx);
1280 if (i >= 0) {
1281 break;
1282 }
1283 character = source_row.length;
1284 comment(source_row);
1285 from = 0;
1286 if (!next_line()) {
1287 stop('unclosed_comment', line, character);
1288 }
1289 }
1290 comment(source_row.slice(0, i), '/*');
1291 character += i + 2;
1292 if (source_row.charAt(i) === '/') {
1293 stop('nested_comment', line, character);
1294 }
1295 source_row = source_row.slice(i + 2);
1296 break;
1297
1298 case '':
1299 break;
1300 // /
1301 case '/':
1302 if (token.id === '/=') {
1303 stop('slash_equal', line, from);
1304 }
1305 return prereg
1306 ? regexp()
1307 : it('(punctuator)', snippet);
1308
1309 // punctuator
1310 default:
1311 return it('(punctuator)', snippet);
1312 }
1313 }
1314 }
1315 }
1316 };
1317 }());
1318
1319 function define(kind, token) {
1320
1321 // Define a name.
1322
1323 var name = token.string,
1324 master = scope[name]; // The current definition of the name
1325
1326 // vars are created with a deadzone, so that the expression that initializes
1327 // the var cannot access the var. Functions are not writeable.
1328
1329 token.dead = false;
1330 token.init = false;
1331 token.kind = kind;
1332 token.master = master;
1333 token.used = 0;
1334 token.writeable = true;
1335
1336 // Global variables are a little weird. They can be defined multiple times.
1337 // Some predefined global vars are (or should) not be writeable.
1338
1339 if (kind === 'var' && funct === global_funct) {
1340 if (!master) {
1341 if (predefined[name] === false) {
1342 token.writeable = false;
1343 }
1344 global_scope[name] = token;
1345 }
1346 } else {
1347
1348 // It is an error if the name has already been defined in this scope, except
1349 // when reusing an exception variable name.
1350
1351 if (master) {
1352 if (master.function === funct) {
1353 if (master.kind !== 'exception' || kind !== 'exception' ||
1354 !master.dead) {
1355 token.warn('already_defined', name);
1356 }
1357 } else if (master.function !== global_funct) {
1358 if (kind === 'var') {
1359 token.warn('redefinition_a_b', name, master.line);
1360 }
1361 }
1362 }
1363 scope[name] = token;
1364 if (kind === 'var') {
1365 block_var.push(name);
1366 }
1367 }
1368 }
1369
1370 function peek(distance) {
1371
1372 // Peek ahead to a future token. The distance is how far ahead to look. The
1373 // default is the next token.
1374
1375 var found, slot = 0;
1376
1377 distance = distance || 0;
1378 while (slot <= distance) {
1379 found = lookahead[slot];
1380 if (!found) {
1381 found = lookahead[slot] = lex.token();
1382 }
1383 slot += 1;
1384 }
1385 return found;
1386 }
1387
1388
1389 function advance(id, match) {
1390
1391 // Produce the next token, also looking for programming errors.
1392
1393 if (indent) {
1394
1395 // If indentation checking was requested, then inspect all of the line breakings.
1396 // The var statement is tricky because the names might be aligned or not. We
1397 // look at the first line break after the var to determine the programmer's
1398 // intention.
1399
1400 if (var_mode && next_token.line !== token.line) {
1401 if ((var_mode !== indent || !next_token.edge) &&
1402 next_token.from === indent.at -
1403 (next_token.edge ? option.indent : 0)) {
1404 var dent = indent;
1405 for (;;) {
1406 dent.at -= option.indent;
1407 if (dent === var_mode) {
1408 break;
1409 }
1410 dent = dent.was;
1411 }
1412 dent.open = false;
1413 }
1414 var_mode = null;
1415 }
1416 if (next_token.id === '?' && indent.mode === ':' &&
1417 token.line !== next_token.line) {
1418 indent.at -= option.indent;
1419 }
1420 if (indent.open) {
1421
1422 // If the token is an edge.
1423
1424 if (next_token.edge) {
1425 if (next_token.edge === 'label') {
1426 expected_at(1);
1427 } else if (next_token.edge === 'case' || indent.mode === 'statement') {
1428 expected_at(indent.at - option.indent);
1429 } else if (indent.mode !== 'array' || next_token.line !== token.line) {
1430 expected_at(indent.at);
1431 }
1432
1433 // If the token is not an edge, but is the first token on the line.
1434
1435 } else if (next_token.line !== token.line) {
1436 if (next_token.from < indent.at + (indent.mode ===
1437 'expression' ? 0 : option.indent)) {
1438 expected_at(indent.at + option.indent);
1439 }
1440 indent.wrap = true;
1441 }
1442 } else if (next_token.line !== token.line) {
1443 if (next_token.edge) {
1444 expected_at(indent.at);
1445 } else {
1446 indent.wrap = true;
1447 if (indent.mode === 'statement' || indent.mode === 'var') {
1448 expected_at(indent.at + option.indent);
1449 } else if (next_token.from < indent.at + (indent.mode ===
1450 'expression' ? 0 : option.indent)) {
1451 expected_at(indent.at + option.indent);
1452 }
1453 }
1454 }
1455 }
1456
1457 switch (token.id) {
1458 case '(number)':
1459 if (next_token.id === '.') {
1460 next_token.warn('trailing_decimal_a');
1461 }
1462 break;
1463 case '-':
1464 if (next_token.id === '-' || next_token.id === '--') {
1465 next_token.warn('confusing_a');
1466 }
1467 break;
1468 case '+':
1469 if (next_token.id === '+' || next_token.id === '++') {
1470 next_token.warn('confusing_a');
1471 }
1472 break;
1473 }
1474 if (token.id === '(string)' || token.identifier) {
1475 anonname = token.string;
1476 }
1477
1478 if (id && next_token.id !== id) {
1479 if (match) {
1480 next_token.warn('expected_a_b_from_c_d', id,
1481 match.id, match.line, artifact());
1482 } else if (!next_token.identifier || next_token.string !== id) {
1483 next_token.warn('expected_a_b', id, artifact());
1484 }
1485 }
1486 prev_token = token;
1487 token = next_token;
1488 next_token = lookahead.shift() || lex.token();
1489 next_token.function = funct;
1490 tokens.push(next_token);
1491 }
1492
1493
1494 function do_globals() {
1495 var name, writeable;
1496 for (;;) {
1497 if (next_token.id !== '(string)' && !next_token.identifier) {
1498 return;
1499 }
1500 name = next_token.string;
1501 advance();
1502 writeable = false;
1503 if (next_token.id === ':') {
1504 advance(':');
1505 switch (next_token.id) {
1506 case 'true':
1507 writeable = predefined[name] !== false;
1508 advance('true');
1509 break;
1510 case 'false':
1511 advance('false');
1512 break;
1513 default:
1514 next_token.stop('unexpected_a');
1515 }
1516 }
1517 predefined[name] = writeable;
1518 if (next_token.id !== ',') {
1519 return;
1520 }
1521 advance(',');
1522 }
1523 }
1524
1525
1526 function do_jslint() {
1527 var name, value;
1528 while (next_token.id === '(string)' || next_token.identifier) {
1529 name = next_token.string;
1530 if (!allowed_option[name]) {
1531 next_token.stop('unexpected_a');
1532 }
1533 advance();
1534 if (next_token.id !== ':') {
1535 next_token.stop('expected_a_b', ':', artifact());
1536 }
1537 advance(':');
1538 if (typeof allowed_option[name] === 'number') {
1539 value = next_token.number;
1540 if (value > allowed_option[name] || value <= 0 ||
1541 Math.floor(value) !== value) {
1542 next_token.stop('expected_small_a');
1543 }
1544 option[name] = value;
1545 } else {
1546 if (next_token.id === 'true') {
1547 option[name] = true;
1548 } else if (next_token.id === 'false') {
1549 option[name] = false;
1550 } else {
1551 next_token.stop('unexpected_a');
1552 }
1553 }
1554 advance();
1555 if (next_token.id === ',') {
1556 advance(',');
1557 }
1558 }
1559 assume();
1560 }
1561
1562
1563 function do_properties() {
1564 var name;
1565 option.properties = true;
1566 for (;;) {
1567 if (next_token.id !== '(string)' && !next_token.identifier) {
1568 return;
1569 }
1570 name = next_token.string;
1571 advance();
1572 if (next_token.id === ':') {
1573 for (;;) {
1574 advance();
1575 if (next_token.id !== '(string)' && !next_token.identifier) {
1576 break;
1577 }
1578 }
1579 }
1580 property[name] = 0;
1581 if (next_token.id !== ',') {
1582 return;
1583 }
1584 advance(',');
1585 }
1586 }
1587
1588
1589 directive = function directive() {
1590 var command = this.id,
1591 old_comments_off = comments_off,
1592 old_indent = indent;
1593 comments_off = true;
1594 indent = null;
1595 if (next_token.line === token.line && next_token.from === token.thru) {
1596 next_token.warn('missing_space_a_b', artifact(token), artifact());
1597 }
1598 if (lookahead.length > 0) {
1599 this.warn('unexpected_a');
1600 }
1601 switch (command) {
1602 case '/*properties':
1603 case '/*property':
1604 case '/*members':
1605 case '/*member':
1606 do_properties();
1607 break;
1608 case '/*jslint':
1609 do_jslint();
1610 break;
1611 case '/*globals':
1612 case '/*global':
1613 do_globals();
1614 break;
1615 default:
1616 this.stop('unexpected_a');
1617 }
1618 comments_off = old_comments_off;
1619 advance('*/');
1620 indent = old_indent;
1621 };
1622
1623
1624 // Indentation intention
1625
1626 function edge(mode) {
1627 next_token.edge = indent ? indent.open && (mode || 'edge') : '';
1628 }
1629
1630
1631 function step_in(mode) {
1632 var open;
1633 if (typeof mode === 'number') {
1634 indent = {
1635 at: +mode,
1636 open: true,
1637 was: indent
1638 };
1639 } else if (!indent) {
1640 indent = {
1641 at: 1,
1642 mode: 'statement',
1643 open: true
1644 };
1645 } else if (mode === 'statement') {
1646 indent = {
1647 at: indent.at,
1648 open: true,
1649 was: indent
1650 };
1651 } else {
1652 open = mode === 'var' || next_token.line !== token.line;
1653 indent = {
1654 at: (open || mode === 'control'
1655 ? indent.at + option.indent
1656 : indent.at) + (indent.wrap ? option.indent : 0),
1657 mode: mode,
1658 open: open,
1659 was: indent
1660 };
1661 if (mode === 'var' && open) {
1662 var_mode = indent;
1663 }
1664 }
1665 }
1666
1667 function step_out(id, symbol) {
1668 if (id) {
1669 if (indent && indent.open) {
1670 indent.at -= option.indent;
1671 edge();
1672 }
1673 advance(id, symbol);
1674 }
1675 if (indent) {
1676 indent = indent.was;
1677 }
1678 }
1679
1680 // Functions for conformance of whitespace.
1681
1682 function one_space(left, right) {
1683 left = left || token;
1684 right = right || next_token;
1685 if (right.id !== '(end)' && !option.white &&
1686 (token.line !== right.line ||
1687 token.thru + 1 !== right.from)) {
1688 right.warn('expected_space_a_b', artifact(token), artifact(right));
1689 }
1690 }
1691
1692 function one_space_only(left, right) {
1693 left = left || token;
1694 right = right || next_token;
1695 if (right.id !== '(end)' && (left.line !== right.line ||
1696 (!option.white && left.thru + 1 !== right.from))) {
1697 right.warn('expected_space_a_b', artifact(left), artifact(right));
1698 }
1699 }
1700
1701 function no_space(left, right) {
1702 left = left || token;
1703 right = right || next_token;
1704 if ((!option.white) &&
1705 left.thru !== right.from && left.line === right.line) {
1706 right.warn('unexpected_space_a_b', artifact(left), artifact(right));
1707 }
1708 }
1709
1710 function no_space_only(left, right) {
1711 left = left || token;
1712 right = right || next_token;
1713 if (right.id !== '(end)' && (left.line !== right.line ||
1714 (!option.white && left.thru !== right.from))) {
1715 right.warn('unexpected_space_a_b', artifact(left), artifact(right));
1716 }
1717 }
1718
1719 function spaces(left, right) {
1720 if (!option.white) {
1721 left = left || token;
1722 right = right || next_token;
1723 if (left.thru === right.from && left.line === right.line) {
1724 right.warn('missing_space_a_b', artifact(left), artifact(right));
1725 }
1726 }
1727 }
1728
1729 function comma() {
1730 if (next_token.id !== ',') {
1731 warn('expected_a_b', token.line, token.thru, ',', artifact());
1732 } else {
1733 if (!option.white) {
1734 no_space_only();
1735 }
1736 advance(',');
1737 spaces();
1738 }
1739 }
1740
1741
1742 function semicolon() {
1743 if (next_token.id !== ';') {
1744 warn('expected_a_b', token.line, token.thru, ';', artifact());
1745 } else {
1746 if (!option.white) {
1747 no_space_only();
1748 }
1749 advance(';');
1750 if (semicolon_coda[next_token.id] !== true) {
1751 spaces();
1752 }
1753 }
1754 }
1755
1756 function use_strict() {
1757 if (next_token.string === 'use strict') {
1758 if (strict_mode) {
1759 next_token.warn('unnecessary_use');
1760 }
1761 edge();
1762 advance();
1763 semicolon();
1764 strict_mode = true;
1765 return true;
1766 }
1767 return false;
1768 }
1769
1770
1771 function are_similar(a, b) {
1772 if (a === b) {
1773 return true;
1774 }
1775 if (Array.isArray(a)) {
1776 if (Array.isArray(b) && a.length === b.length) {
1777 var i;
1778 for (i = 0; i < a.length; i += 1) {
1779 if (!are_similar(a[i], b[i])) {
1780 return false;
1781 }
1782 }
1783 return true;
1784 }
1785 return false;
1786 }
1787 if (Array.isArray(b)) {
1788 return false;
1789 }
1790 if (a.id === '(number)' && b.id === '(number)') {
1791 return a.number === b.number;
1792 }
1793 if (a.arity === b.arity && a.string === b.string) {
1794 switch (a.arity) {
1795 case undefined:
1796 return a.string === b.string;
1797 case 'prefix':
1798 case 'suffix':
1799 return a.id === b.id && are_similar(a.first, b.first) &&
1800 a.id !== '{' && a.id !== '[';
1801 case 'infix':
1802 return are_similar(a.first, b.first) &&
1803 are_similar(a.second, b.second);
1804 case 'ternary':
1805 return are_similar(a.first, b.first) &&
1806 are_similar(a.second, b.second) &&
1807 are_similar(a.third, b.third);
1808 case 'function':
1809 case 'regexp':
1810 return false;
1811 default:
1812 return true;
1813 }
1814 }
1815 if (a.id === '.' && b.id === '[' && b.arity === 'infix') {
1816 return a.second.string === b.second.string && b.second.id === '(string)';
1817 }
1818 if (a.id === '[' && a.arity === 'infix' && b.id === '.') {
1819 return a.second.string === b.second.string && a.second.id === '(string)';
1820 }
1821 return false;
1822 }
1823
1824
1825 // This is the heart of JSLINT, the Pratt parser. In addition to parsing, it
1826 // is looking for ad hoc lint patterns. We add .fud to Pratt's model, which is
1827 // like .nud except that it is only used on the first token of a statement.
1828 // Having .fud makes it much easier to define statement-oriented languages like
1829 // JavaScript. I retained Pratt's nomenclature.
1830
1831 // .nud Null denotation
1832 // .fud First null denotation
1833 // .led Left denotation
1834 // lbp Left binding power
1835 // rbp Right binding power
1836
1837 // They are elements of the parsing method called Top Down Operator Precedence.
1838
1839 function expression(rbp, initial) {
1840
1841 // rbp is the right binding power.
1842 // initial indicates that this is the first expression of a statement.
1843
1844 var left;
1845 if (next_token.id === '(end)') {
1846 token.stop('unexpected_a', next_token.id);
1847 }
1848 advance();
1849 if (initial) {
1850 anonname = 'anonymous';
1851 }
1852 if (initial === true && token.fud) {
1853 left = token.fud();
1854 } else {
1855 if (token.nud) {
1856 left = token.nud();
1857 } else {
1858 if (next_token.id === '(number)' && token.id === '.') {
1859 token.warn('leading_decimal_a', artifact());
1860 advance();
1861 return token;
1862 }
1863 token.stop('expected_identifier_a', artifact(token));
1864 }
1865 while (rbp < next_token.lbp) {
1866 advance();
1867 left = token.led(left);
1868 }
1869 }
1870 if (left && left.assign && !initial) {
1871 if (!option.ass) {
1872 left.warn('assignment_expression');
1873 }
1874 if (left.id !== '=' && left.first.master) {
1875 left.first.master.used = true;
1876 }
1877 }
1878 return left;
1879 }
1880
1881 protosymbol = {
1882 nud: function () {
1883 this.stop('unexpected_a');
1884 },
1885 led: function () {
1886 this.stop('expected_operator_a');
1887 },
1888 warn: function (code, a, b, c, d) {
1889 if (!this.warning) {
1890 this.warning = warn(code, this.line || 0, this.from || 0,
1891 a || artifact(this), b, c, d);
1892 }
1893 },
1894 stop: function (code, a, b, c, d) {
1895 this.warning = undefined;
1896 this.warn(code, a, b, c, d);
1897 return quit('stopping', this.line, this.character);
1898 },
1899 lbp: 0
1900 };
1901
1902 // Functional constructors for making the symbols that will be inherited by
1903 // tokens.
1904
1905 function symbol(s, bp) {
1906 var x = syntax[s];
1907 if (!x) {
1908 x = Object.create(protosymbol);
1909 x.id = x.string = s;
1910 x.lbp = bp || 0;
1911 syntax[s] = x;
1912 }
1913 return x;
1914 }
1915
1916 function postscript(x) {
1917 x.postscript = true;
1918 return x;
1919 }
1920
1921 function ultimate(s) {
1922 var x = symbol(s, 0);
1923 x.from = 1;
1924 x.thru = 1;
1925 x.line = 0;
1926 x.edge = 'edge';
1927 x.string = s;
1928 return postscript(x);
1929 }
1930
1931 function reserve_name(x) {
1932 var c = x.id.charAt(0);
1933 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
1934 x.identifier = x.reserved = true;
1935 }
1936 return x;
1937 }
1938
1939 function stmt(s, f) {
1940 var x = symbol(s);
1941 x.fud = f;
1942 return reserve_name(x);
1943 }
1944
1945 function disrupt_stmt(s, f) {
1946 var x = stmt(s, f);
1947 x.disrupt = true;
1948 }
1949
1950 function labeled_stmt(s, f) {
1951 var x = stmt(s, function labeled() {
1952 var the_statement;
1953 if (funct.breakage) {
1954 funct.breakage.push(this);
1955 } else {
1956 funct.breakage = [this];
1957 }
1958 the_statement = f.apply(this);
1959 if (funct.breakage.length > 1) {
1960 funct.breakage.pop();
1961 } else {
1962 delete funct.breakage;
1963 }
1964 return the_statement;
1965 });
1966 x.labeled = true;
1967 }
1968
1969 function prefix(s, f) {
1970 var x = symbol(s, 150);
1971 reserve_name(x);
1972 x.nud = function () {
1973 var that = this;
1974 that.arity = 'prefix';
1975 if (typeof f === 'function') {
1976 that = f(that);
1977 if (that.arity !== 'prefix') {
1978 return that;
1979 }
1980 } else {
1981 if (s === 'typeof') {
1982 one_space();
1983 } else {
1984 no_space_only();
1985 }
1986 that.first = expression(150);
1987 }
1988 switch (that.id) {
1989 case '++':
1990 case '--':
1991 if (!option.plusplus) {
1992 that.warn('unexpected_a');
1993 } else if ((!that.first.identifier || that.first.reserved) &&
1994 that.first.id !== '.' && that.first.id !== '[') {
1995 that.warn('bad_operand');
1996 }
1997 break;
1998 default:
1999 if (that.first.arity === 'prefix' ||
2000 that.first.arity === 'function') {
2001 that.warn('unexpected_a');
2002 }
2003 }
2004 return that;
2005 };
2006 return x;
2007 }
2008
2009
2010 function type(s, t, nud) {
2011 var x = symbol(s);
2012 x.arity = t;
2013 if (nud) {
2014 x.nud = nud;
2015 }
2016 return x;
2017 }
2018
2019
2020 function reserve(s, f) {
2021 var x = symbol(s);
2022 x.identifier = x.reserved = true;
2023 if (typeof f === 'function') {
2024 x.nud = f;
2025 }
2026 return x;
2027 }
2028
2029
2030 function constant(name) {
2031 var x = reserve(name);
2032 x.string = name;
2033 x.nud = return_this;
2034 return x;
2035 }
2036
2037
2038 function reservevar(s, v) {
2039 return reserve(s, function () {
2040 if (typeof v === 'function') {
2041 v(this);
2042 }
2043 return this;
2044 });
2045 }
2046
2047
2048 function infix(s, p, f, w) {
2049 var x = symbol(s, p);
2050 reserve_name(x);
2051 x.led = function (left) {
2052 this.arity = 'infix';
2053 if (!w) {
2054 spaces(prev_token, token);
2055 spaces();
2056 }
2057 if (!option.bitwise && this.bitwise) {
2058 this.warn('unexpected_a');
2059 }
2060 if (typeof f === 'function') {
2061 return f(left, this);
2062 }
2063 this.first = left;
2064 this.second = expression(p);
2065 return this;
2066 };
2067 return x;
2068 }
2069
2070 function expected_relation(node, message) {
2071 if (node.assign) {
2072 node.warn(message || 'conditional_assignment');
2073 }
2074 return node;
2075 }
2076
2077 function expected_condition(node, message) {
2078 switch (node.id) {
2079 case '[':
2080 case '-':
2081 if (node.arity !== 'infix') {
2082 node.warn(message || 'weird_condition');
2083 }
2084 break;
2085 case 'false':
2086 case 'function':
2087 case 'Infinity':
2088 case 'NaN':
2089 case 'null':
2090 case 'true':
2091 case 'undefined':
2092 case 'void':
2093 case '(number)':
2094 case '(regexp)':
2095 case '(string)':
2096 case '{':
2097 case '?':
2098 case '~':
2099 node.warn(message || 'weird_condition');
2100 break;
2101 case '(':
2102 if (node.first.id === 'new' ||
2103 (node.first.string === 'Boolean') ||
2104 (node.first.id === '.' &&
2105 numbery[node.first.second.string] === true)) {
2106 node.warn(message || 'weird_condition');
2107 }
2108 break;
2109 }
2110 return node;
2111 }
2112
2113 function check_relation(node) {
2114 switch (node.arity) {
2115 case 'prefix':
2116 switch (node.id) {
2117 case '{':
2118 case '[':
2119 node.warn('unexpected_a');
2120 break;
2121 case '!':
2122 node.warn('confusing_a');
2123 break;
2124 }
2125 break;
2126 case 'function':
2127 case 'regexp':
2128 node.warn('unexpected_a');
2129 break;
2130 default:
2131 if (node.id === 'NaN') {
2132 node.warn('isNaN');
2133 } else if (node.relation) {
2134 node.warn('weird_relation');
2135 }
2136 }
2137 return node;
2138 }
2139
2140
2141 function relation(s, eqeq) {
2142 var x = infix(s, 100, function (left, that) {
2143 check_relation(left);
2144 if (eqeq && !option.eqeq) {
2145 that.warn('expected_a_b', eqeq, that.id);
2146 }
2147 var right = expression(100);
2148 if (are_similar(left, right) ||
2149 ((left.id === '(string)' || left.id === '(number)') &&
2150 (right.id === '(string)' || right.id === '(number)'))) {
2151 that.warn('weird_relation');
2152 } else if (left.id === 'typeof') {
2153 if (right.id !== '(string)') {
2154 right.warn("expected_string_a", artifact(right));
2155 } else if (right.string === 'undefined' ||
2156 right.string === 'null') {
2157 left.warn("unexpected_typeof_a", right.string);
2158 }
2159 } else if (right.id === 'typeof') {
2160 if (left.id !== '(string)') {
2161 left.warn("expected_string_a", artifact(left));
2162 } else if (left.string === 'undefined' ||
2163 left.string === 'null') {
2164 right.warn("unexpected_typeof_a", left.string);
2165 }
2166 }
2167 that.first = left;
2168 that.second = check_relation(right);
2169 return that;
2170 });
2171 x.relation = true;
2172 return x;
2173 }
2174
2175 function lvalue(that, s) {
2176 var master;
2177 if (that.identifier) {
2178 master = scope[that.string];
2179 if (master) {
2180 if (scope[that.string].writeable !== true) {
2181 that.warn('read_only');
2182 }
2183 master.used -= 1;
2184 if (s === '=') {
2185 master.init = true;
2186 }
2187 } else if (that.reserved) {
2188 that.warn('expected_identifier_a_reserved');
2189 }
2190 } else if (that.id === '.' || that.id === '[') {
2191 if (!that.first || that.first.string === 'arguments') {
2192 that.warn('bad_assignment');
2193 }
2194 } else {
2195 that.warn('bad_assignment');
2196 }
2197 }
2198
2199
2200 function assignop(s, op) {
2201 var x = infix(s, 20, function (left, that) {
2202 var next;
2203 that.first = left;
2204 lvalue(left, s);
2205 that.second = expression(20);
2206 if (that.id === '=' && are_similar(that.first, that.second)) {
2207 that.warn('weird_assignment');
2208 }
2209 next = that;
2210 while (next_token.id === '=') {
2211 lvalue(next.second, '=');
2212 next_token.first = next.second;
2213 next.second = next_token;
2214 next = next_token;
2215 advance('=');
2216 next.second = expression(20);
2217 }
2218 return that;
2219 });
2220 x.assign = true;
2221 if (op) {
2222 if (syntax[op].bitwise) {
2223 x.bitwise = true;
2224 }
2225 }
2226 return x;
2227 }
2228
2229
2230 function bitwise(s, p) {
2231 var x = infix(s, p, 'number');
2232 x.bitwise = true;
2233 return x;
2234 }
2235
2236
2237 function suffix(s) {
2238 var x = symbol(s, 150);
2239 x.led = function (left) {
2240 no_space_only(prev_token, token);
2241 if (!option.plusplus) {
2242 this.warn('unexpected_a');
2243 } else if ((!left.identifier || left.reserved) &&
2244 left.id !== '.' && left.id !== '[') {
2245 this.warn('bad_operand');
2246 }
2247 this.first = left;
2248 this.arity = 'suffix';
2249 return this;
2250 };
2251 return x;
2252 }
2253
2254
2255 function optional_identifier(variable) {
2256 if (next_token.identifier) {
2257 advance();
2258 if (token.reserved && variable) {
2259 token.warn('expected_identifier_a_reserved');
2260 }
2261 return token.string;
2262 }
2263 }
2264
2265
2266 function identifier(variable) {
2267 var i = optional_identifier(variable);
2268 if (!i) {
2269 next_token.stop(token.id === 'function' && next_token.id === '('
2270 ? 'name_function'
2271 : 'expected_identifier_a');
2272 }
2273 return i;
2274 }
2275
2276
2277 function statement() {
2278
2279 var label, preamble, the_statement;
2280
2281 // We don't like the empty statement.
2282
2283 if (next_token.id === ';') {
2284 next_token.warn('unexpected_a');
2285 semicolon();
2286 return;
2287 }
2288
2289 // Is this a labeled statement?
2290
2291 if (next_token.identifier && !next_token.reserved && peek().id === ':') {
2292 edge('label');
2293 label = next_token;
2294 advance();
2295 advance(':');
2296 define('label', label);
2297 if (next_token.labeled !== true || funct === global_funct) {
2298 label.stop('unexpected_label_a');
2299 } else if (jx.test(label.string + ':')) {
2300 label.warn('url');
2301 }
2302 next_token.label = label;
2303 label.init = true;
2304 label.statement = next_token;
2305 }
2306
2307 // Parse the statement.
2308
2309 preamble = next_token;
2310 if (token.id !== 'else') {
2311 edge();
2312 }
2313 step_in('statement');
2314 the_statement = expression(0, true);
2315 if (the_statement) {
2316
2317 // Look for the final semicolon.
2318
2319 if (the_statement.arity === 'statement') {
2320 if (the_statement.id === 'switch' ||
2321 (the_statement.block && the_statement.id !== 'do')) {
2322 spaces();
2323 } else {
2324 semicolon();
2325 }
2326 } else {
2327
2328 // If this is an expression statement, determine if it is acceptable.
2329 // We do not like
2330 // new Blah;
2331 // statements. If it is to be used at all, new should only be used to make
2332 // objects, not side effects. The expression statements we do like do
2333 // assignment or invocation or delete.
2334
2335 if (the_statement.id === '(') {
2336 if (the_statement.first.id === 'new') {
2337 next_token.warn('bad_new');
2338 }
2339 } else if (the_statement.id === '++' ||
2340 the_statement.id === '--') {
2341 lvalue(the_statement.first);
2342 } else if (!the_statement.assign &&
2343 the_statement.id !== 'delete') {
2344 if (!option.closure || !preamble.comments) {
2345 preamble.warn('assignment_function_expression');
2346 }
2347 }
2348 semicolon();
2349 }
2350 }
2351 step_out();
2352 if (label) {
2353 label.dead = true;
2354 }
2355 return the_statement;
2356 }
2357
2358
2359 function statements() {
2360 var array = [], disruptor, the_statement;
2361
2362 // A disrupt statement may not be followed by any other statement.
2363 // If the last statement is disrupt, then the sequence is disrupt.
2364
2365 while (next_token.postscript !== true) {
2366 if (next_token.id === ';') {
2367 next_token.warn('unexpected_a');
2368 semicolon();
2369 } else {
2370 if (next_token.string === 'use strict') {
2371 if ((!node_js) || funct !== global_funct || array.length > 0) {
2372 next_token.warn('function_strict');
2373 }
2374 use_strict();
2375 }
2376 if (disruptor) {
2377 next_token.warn('unreachable_a_b', next_token.string,
2378 disruptor.string);
2379 disruptor = null;
2380 }
2381 the_statement = statement();
2382 if (the_statement) {
2383 array.push(the_statement);
2384 if (the_statement.disrupt) {
2385 disruptor = the_statement;
2386 array.disrupt = true;
2387 }
2388 }
2389 }
2390 }
2391 return array;
2392 }
2393
2394
2395 function block(kind) {
2396
2397 // A block is a sequence of statements wrapped in braces.
2398
2399 var array,
2400 curly = next_token,
2401 old_block_var = block_var,
2402 old_in_block = in_block,
2403 old_strict_mode = strict_mode;
2404
2405 in_block = kind !== 'function' && kind !== 'try' && kind !== 'catch';
2406 block_var = [];
2407 if (curly.id === '{') {
2408 spaces();
2409 advance('{');
2410 step_in();
2411 if (kind === 'function' && !use_strict() && !old_strict_mode &&
2412 !option.sloppy && funct.level === 1) {
2413 next_token.warn('missing_use_strict');
2414 }
2415 array = statements();
2416 strict_mode = old_strict_mode;
2417 step_out('}', curly);
2418 } else if (in_block) {
2419 curly.stop('expected_a_b', '{', artifact());
2420 } else {
2421 curly.warn('expected_a_b', '{', artifact());
2422 array = [statement()];
2423 array.disrupt = array[0].disrupt;
2424 }
2425 if (kind !== 'catch' && array.length === 0 && !option.debug) {
2426 curly.warn('empty_block');
2427 }
2428 block_var.forEach(function (name) {
2429 scope[name].dead = true;
2430 });
2431 block_var = old_block_var;
2432 in_block = old_in_block;
2433 return array;
2434 }
2435
2436
2437 function tally_property(name) {
2438 if (option.properties && typeof property[name] !== 'number') {
2439 token.warn('unexpected_property_a', name);
2440 }
2441 if (property[name]) {
2442 property[name] += 1;
2443 } else {
2444 property[name] = 1;
2445 }
2446 }
2447
2448
2449 // ECMAScript parser
2450
2451 (function () {
2452 var x = symbol('(identifier)');
2453 x.nud = function () {
2454 var name = this.string,
2455 master = scope[name],
2456 writeable;
2457
2458 // If the master is not in scope, then we may have an undeclared variable.
2459 // Check the predefined list. If it was predefined, create the global
2460 // variable.
2461
2462 if (!master) {
2463 writeable = predefined[name];
2464 if (typeof writeable === 'boolean') {
2465 global_scope[name] = master = {
2466 dead: false,
2467 function: global_funct,
2468 kind: 'var',
2469 string: name,
2470 writeable: writeable
2471 };
2472
2473 // But if the variable is not in scope, and is not predefined, and if we are not
2474 // in the global scope, then we have an undefined variable error.
2475
2476 } else {
2477 token.warn('used_before_a');
2478 }
2479 } else {
2480 this.master = master;
2481 }
2482
2483 // Annotate uses that cross scope boundaries.
2484
2485 if (master) {
2486 if (master.kind === 'label') {
2487 this.warn('a_label');
2488 } else {
2489 if (master.dead === true || master.dead === funct) {
2490 this.warn('a_scope');
2491 }
2492 master.used += 1;
2493 if (master.function !== funct) {
2494 if (master.function === global_funct) {
2495 funct.global.push(name);
2496 } else {
2497 master.function.closure.push(name);
2498 funct.outer.push(name);
2499 }
2500 }
2501 }
2502 }
2503 return this;
2504 };
2505 x.identifier = true;
2506 }());
2507
2508
2509 // Build the syntax table by declaring the syntactic elements.
2510
2511 type('(array)', 'array');
2512 type('(function)', 'function');
2513 type('(number)', 'number', return_this);
2514 type('(object)', 'object');
2515 type('(string)', 'string', return_this);
2516 type('(boolean)', 'boolean', return_this);
2517 type('(regexp)', 'regexp', return_this);
2518
2519 ultimate('(begin)');
2520 ultimate('(end)');
2521 ultimate('(error)');
2522 postscript(symbol('}'));
2523 symbol(')');
2524 symbol(']');
2525 postscript(symbol('"'));
2526 postscript(symbol('\''));
2527 symbol(';');
2528 symbol(':');
2529 symbol(',');
2530 symbol('#');
2531 symbol('@');
2532 symbol('*/');
2533 postscript(reserve('case'));
2534 reserve('catch');
2535 postscript(reserve('default'));
2536 reserve('else');
2537 reserve('finally');
2538
2539 reservevar('arguments', function (x) {
2540 if (strict_mode && funct === global_funct) {
2541 x.warn('strict');
2542 }
2543 funct.arguments = true;
2544 });
2545 reservevar('eval');
2546 constant('false', 'boolean');
2547 constant('Infinity', 'number');
2548 constant('NaN', 'number');
2549 constant('null', '');
2550 reservevar('this', function (x) {
2551 if (strict_mode && funct.statement && funct.name.charAt(0) > 'Z') {
2552 x.warn('strict');
2553 }
2554 });
2555 constant('true', 'boolean');
2556 constant('undefined', '');
2557
2558 infix('?', 30, function (left, that) {
2559 step_in('?');
2560 that.first = expected_condition(expected_relation(left));
2561 that.second = expression(0);
2562 spaces();
2563 step_out();
2564 var colon = next_token;
2565 advance(':');
2566 step_in(':');
2567 spaces();
2568 that.third = expression(10);
2569 that.arity = 'ternary';
2570 if (are_similar(that.second, that.third)) {
2571 colon.warn('weird_ternary');
2572 } else if (are_similar(that.first, that.second)) {
2573 that.warn('use_or');
2574 }
2575 step_out();
2576 return that;
2577 });
2578
2579 infix('||', 40, function (left, that) {
2580 function paren_check(that) {
2581 if (that.id === '&&' && !that.paren) {
2582 that.warn('and');
2583 }
2584 return that;
2585 }
2586
2587 that.first = paren_check(expected_condition(expected_relation(left)));
2588 that.second = paren_check(expected_relation(expression(40)));
2589 if (are_similar(that.first, that.second)) {
2590 that.warn('weird_condition');
2591 }
2592 return that;
2593 });
2594
2595 infix('&&', 50, function (left, that) {
2596 that.first = expected_condition(expected_relation(left));
2597 that.second = expected_relation(expression(50));
2598 if (are_similar(that.first, that.second)) {
2599 that.warn('weird_condition');
2600 }
2601 return that;
2602 });
2603
2604 prefix('void', function (that) {
2605 that.first = expression(0);
2606 that.warn('expected_a_b', 'undefined', 'void');
2607 return that;
2608 });
2609
2610 bitwise('|', 70);
2611 bitwise('^', 80);
2612 bitwise('&', 90);
2613
2614 relation('==', '===');
2615 relation('===');
2616 relation('!=', '!==');
2617 relation('!==');
2618 relation('<');
2619 relation('>');
2620 relation('<=');
2621 relation('>=');
2622
2623 bitwise('<<', 120);
2624 bitwise('>>', 120);
2625 bitwise('>>>', 120);
2626
2627 infix('in', 120, function (left, that) {
2628 that.warn('infix_in');
2629 that.left = left;
2630 that.right = expression(130);
2631 return that;
2632 });
2633 infix('instanceof', 120);
2634 infix('+', 130, function (left, that) {
2635 if (left.id === '(number)') {
2636 if (left.number === 0) {
2637 left.warn('unexpected_a', '0');
2638 }
2639 } else if (left.id === '(string)') {
2640 if (left.string === '') {
2641 left.warn('expected_a_b', 'String', '\'\'');
2642 }
2643 }
2644 var right = expression(130);
2645 if (right.id === '(number)') {
2646 if (right.number === 0) {
2647 right.warn('unexpected_a', '0');
2648 }
2649 } else if (right.id === '(string)') {
2650 if (right.string === '') {
2651 right.warn('expected_a_b', 'String', '\'\'');
2652 }
2653 }
2654 if (left.id === right.id) {
2655 if (left.id === '(string)' || left.id === '(number)') {
2656 if (left.id === '(string)') {
2657 left.string += right.string;
2658 if (jx.test(left.string)) {
2659 left.warn('url');
2660 }
2661 } else {
2662 left.number += right.number;
2663 }
2664 left.thru = right.thru;
2665 return left;
2666 }
2667 }
2668 that.first = left;
2669 that.second = right;
2670 return that;
2671 });
2672 prefix('+');
2673 prefix('+++', function () {
2674 token.warn('confusing_a');
2675 this.first = expression(150);
2676 this.arity = 'prefix';
2677 return this;
2678 });
2679 infix('+++', 130, function (left) {
2680 token.warn('confusing_a');
2681 this.first = left;
2682 this.second = expression(130);
2683 return this;
2684 });
2685 infix('-', 130, function (left, that) {
2686 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') {
2687 left.warn('unexpected_a');
2688 }
2689 var right = expression(130);
2690 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') {
2691 right.warn('unexpected_a');
2692 }
2693 if (left.id === right.id && left.id === '(number)') {
2694 left.number -= right.number;
2695 left.thru = right.thru;
2696 return left;
2697 }
2698 that.first = left;
2699 that.second = right;
2700 return that;
2701 });
2702 prefix('-');
2703 prefix('---', function () {
2704 token.warn('confusing_a');
2705 this.first = expression(150);
2706 this.arity = 'prefix';
2707 return this;
2708 });
2709 infix('---', 130, function (left) {
2710 token.warn('confusing_a');
2711 this.first = left;
2712 this.second = expression(130);
2713 return this;
2714 });
2715 infix('*', 140, function (left, that) {
2716 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') {
2717 left.warn('unexpected_a');
2718 }
2719 var right = expression(140);
2720 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') {
2721 right.warn('unexpected_a');
2722 }
2723 if (left.id === right.id && left.id === '(number)') {
2724 left.number *= right.number;
2725 left.thru = right.thru;
2726 return left;
2727 }
2728 that.first = left;
2729 that.second = right;
2730 return that;
2731 });
2732 infix('/', 140, function (left, that) {
2733 if ((left.id === '(number)' && left.number === 0) || left.id === '(string)') {
2734 left.warn('unexpected_a');
2735 }
2736 var right = expression(140);
2737 if ((right.id === '(number)' && (right.number === 0 || right.number === 1)) || right.id === '(string)') {
2738 right.warn('unexpected_a');
2739 }
2740 if (left.id === right.id && left.id === '(number)') {
2741 left.number /= right.number;
2742 left.thru = right.thru;
2743 return left;
2744 }
2745 that.first = left;
2746 that.second = right;
2747 return that;
2748 });
2749 infix('%', 140, function (left, that) {
2750 if ((left.id === '(number)' && (left.number === 0 || left.number === 1)) || left.id === '(string)') {
2751 left.warn('unexpected_a');
2752 }
2753 var right = expression(140);
2754 if ((right.id === '(number)' && right.number === 0) || right.id === '(string)') {
2755 right.warn('unexpected_a');
2756 }
2757 if (left.id === right.id && left.id === '(number)') {
2758 left.number %= right.number;
2759 left.thru = right.thru;
2760 return left;
2761 }
2762 that.first = left;
2763 that.second = right;
2764 return that;
2765 });
2766
2767 suffix('++');
2768 prefix('++');
2769
2770 suffix('--');
2771 prefix('--');
2772 prefix('delete', function (that) {
2773 one_space();
2774 var p = expression(0);
2775 if (!p || (p.id !== '.' && p.id !== '[')) {
2776 next_token.warn('deleted');
2777 }
2778 that.first = p;
2779 return that;
2780 });
2781
2782
2783 prefix('~', function (that) {
2784 no_space_only();
2785 if (!option.bitwise) {
2786 that.warn('unexpected_a');
2787 }
2788 that.first = expression(150);
2789 return that;
2790 });
2791 function banger(that) {
2792 no_space_only();
2793 that.first = expected_condition(expression(150));
2794 if (bang[that.first.id] === that || that.first.assign) {
2795 that.warn('confusing_a');
2796 }
2797 return that;
2798 }
2799 prefix('!', banger);
2800 prefix('!!', banger);
2801 prefix('typeof');
2802 prefix('new', function (that) {
2803 one_space();
2804 var c = expression(160), n, p, v;
2805 that.first = c;
2806 if (c.id !== 'function') {
2807 if (c.identifier) {
2808 switch (c.string) {
2809 case 'Object':
2810 token.warn('use_object');
2811 break;
2812 case 'Array':
2813 if (next_token.id === '(') {
2814 p = next_token;
2815 p.first = this;
2816 advance('(');
2817 if (next_token.id !== ')') {
2818 n = expression(0);
2819 p.second = [n];
2820 if (n.id === '(string)' || next_token.id === ',') {
2821 p.warn('use_array');
2822 }
2823 while (next_token.id === ',') {
2824 advance(',');
2825 p.second.push(expression(0));
2826 }
2827 } else {
2828 token.warn('use_array');
2829 }
2830 advance(')', p);
2831 return p;
2832 }
2833 token.warn('use_array');
2834 break;
2835 case 'Number':
2836 case 'String':
2837 case 'Boolean':
2838 case 'Math':
2839 case 'JSON':
2840 c.warn('not_a_constructor');
2841 break;
2842 case 'Function':
2843 if (!option.evil) {
2844 next_token.warn('function_eval');
2845 }
2846 break;
2847 case 'Date':
2848 case 'RegExp':
2849 case 'this':
2850 break;
2851 default:
2852 if (c.id !== 'function') {
2853 v = c.string.charAt(0);
2854 if (!option.newcap && (v < 'A' || v > 'Z')) {
2855 token.warn('constructor_name_a');
2856 }
2857 }
2858 }
2859 } else {
2860 if (c.id !== '.' && c.id !== '[' && c.id !== '(') {
2861 token.warn('bad_constructor');
2862 }
2863 }
2864 } else {
2865 that.warn('weird_new');
2866 }
2867 if (next_token.id !== '(') {
2868 next_token.warn('missing_a', '()');
2869 }
2870 return that;
2871 });
2872
2873 infix('(', 160, function (left, that) {
2874 var e, p;
2875 if (indent && indent.mode === 'expression') {
2876 no_space(prev_token, token);
2877 } else {
2878 no_space_only(prev_token, token);
2879 }
2880 if (!left.immed && left.id === 'function') {
2881 next_token.warn('wrap_immediate');
2882 }
2883 p = [];
2884 if (left.identifier) {
2885 if (left.string.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {
2886 if (left.string !== 'Number' && left.string !== 'String' &&
2887 left.string !== 'Boolean' && left.string !== 'Date') {
2888 if (left.string === 'Math') {
2889 left.warn('not_a_function');
2890 } else if (left.string === 'Object') {
2891 token.warn('use_object');
2892 } else if (left.string === 'Array' || !option.newcap) {
2893 left.warn('missing_a', 'new');
2894 }
2895 }
2896 } else if (left.string === 'JSON') {
2897 left.warn('not_a_function');
2898 }
2899 } else if (left.id === '.') {
2900 if (left.second.string === 'split' &&
2901 left.first.id === '(string)') {
2902 left.second.warn('use_array');
2903 }
2904 }
2905 step_in();
2906 if (next_token.id !== ')') {
2907 no_space();
2908 for (;;) {
2909 edge();
2910 e = expression(10);
2911 if (left.string === 'Boolean' && (e.id === '!' || e.id === '~')) {
2912 e.warn('weird_condition');
2913 }
2914 p.push(e);
2915 if (next_token.id !== ',') {
2916 break;
2917 }
2918 comma();
2919 }
2920 }
2921 no_space();
2922 step_out(')', that);
2923 if (typeof left === 'object') {
2924 if (left.string === 'parseInt' && p.length === 1) {
2925 left.warn('radix');
2926 } else if (left.string === 'String' && p.length >= 1 && p[0].id === '(string)') {
2927 left.warn('unexpected_a');
2928 }
2929 if (!option.evil) {
2930 if (left.string === 'eval' || left.string === 'Function' ||
2931 left.string === 'execScript') {
2932 left.warn('evil');
2933 } else if (p[0] && p[0].id === '(string)' &&
2934 (left.string === 'setTimeout' ||
2935 left.string === 'setInterval')) {
2936 left.warn('implied_evil');
2937 }
2938 }
2939 if (!left.identifier && left.id !== '.' && left.id !== '[' &&
2940 left.id !== '(' && left.id !== '&&' && left.id !== '||' &&
2941 left.id !== '?') {
2942 left.warn('bad_invocation');
2943 }
2944 if (left.id === '.') {
2945 if (p.length > 0 &&
2946 left.first && left.first.first &&
2947 are_similar(p[0], left.first.first)) {
2948 if (left.second.string === 'call' ||
2949 (left.second.string === 'apply' && (p.length === 1 ||
2950 (p[1].arity === 'prefix' && p[1].id === '[')))) {
2951 left.second.warn('unexpected_a');
2952 }
2953 }
2954 if (left.second.string === 'toString') {
2955 if (left.first.id === '(string)' || left.first.id === '(number)') {
2956 left.second.warn('unexpected_a');
2957 }
2958 }
2959 }
2960 }
2961 that.first = left;
2962 that.second = p;
2963 return that;
2964 }, true);
2965
2966 prefix('(', function (that) {
2967 step_in('expression');
2968 no_space();
2969 edge();
2970 if (next_token.id === 'function') {
2971 next_token.immed = true;
2972 }
2973 var value = expression(0);
2974 value.paren = true;
2975 no_space();
2976 step_out(')', that);
2977 if (value.id === 'function') {
2978 switch (next_token.id) {
2979 case '(':
2980 next_token.warn('move_invocation');
2981 break;
2982 case '.':
2983 case '[':
2984 next_token.warn('unexpected_a');
2985 break;
2986 default:
2987 that.warn('bad_wrap');
2988 }
2989 } else if (!value.arity) {
2990 if (!option.closure || !that.comments) {
2991 that.warn('unexpected_a');
2992 }
2993 }
2994 return value;
2995 });
2996
2997 infix('.', 170, function (left, that) {
2998 no_space(prev_token, token);
2999 no_space();
3000 var name = identifier();
3001 if (typeof name === 'string') {
3002 tally_property(name);
3003 }
3004 that.first = left;
3005 that.second = token;
3006 if (left && left.string === 'arguments' &&
3007 (name === 'callee' || name === 'caller')) {
3008 left.warn('avoid_a', 'arguments.' + name);
3009 } else if (!option.evil && left && left.string === 'document' &&
3010 (name === 'write' || name === 'writeln')) {
3011 left.warn('write_is_wrong');
3012 } else if (!option.stupid && syx.test(name)) {
3013 token.warn('sync_a');
3014 } else if (left && left.id === '{') {
3015 that.warn('unexpected_a');
3016 }
3017 if (!option.evil && (name === 'eval' || name === 'execScript')) {
3018 next_token.warn('evil');
3019 }
3020 return that;
3021 }, true);
3022
3023 infix('[', 170, function (left, that) {
3024 var e, s;
3025 no_space_only(prev_token, token);
3026 no_space();
3027 step_in();
3028 edge();
3029 e = expression(0);
3030 switch (e.id) {
3031 case '(number)':
3032 if (e.id === '(number)' && left.id === 'arguments') {
3033 left.warn('use_param');
3034 }
3035 break;
3036 case '(string)':
3037 if (!option.evil &&
3038 (e.string === 'eval' || e.string === 'execScript')) {
3039 e.warn('evil');
3040 } else if (!option.sub && ix.test(e.string)) {
3041 s = syntax[e.string];
3042 if (!s || !s.reserved) {
3043 e.warn('subscript');
3044 }
3045 }
3046 tally_property(e.string);
3047 break;
3048 }
3049 if (left && (left.id === '{' || (left.id === '[' && left.arity === 'prefix'))) {
3050 that.warn('unexpected_a');
3051 }
3052 step_out(']', that);
3053 no_space(prev_token, token);
3054 that.first = left;
3055 that.second = e;
3056 return that;
3057 }, true);
3058
3059 prefix('[', function (that) {
3060 that.first = [];
3061 step_in('array');
3062 while (next_token.id !== '(end)') {
3063 while (next_token.id === ',') {
3064 next_token.warn('unexpected_a');
3065 advance(',');
3066 }
3067 if (next_token.id === ']') {
3068 break;
3069 }
3070 indent.wrap = false;
3071 edge();
3072 that.first.push(expression(10));
3073 if (next_token.id === ',') {
3074 comma();
3075 if (next_token.id === ']') {
3076 token.warn('unexpected_a');
3077 break;
3078 }
3079 } else {
3080 break;
3081 }
3082 }
3083 step_out(']', that);
3084 return that;
3085 }, 170);
3086
3087
3088 function property_name() {
3089 var id = optional_identifier();
3090 if (!id) {
3091 if (next_token.id === '(string)') {
3092 id = next_token.string;
3093 advance();
3094 } else if (next_token.id === '(number)') {
3095 id = next_token.number.toString();
3096 advance();
3097 }
3098 }
3099 return id;
3100 }
3101
3102
3103
3104 assignop('=');
3105 assignop('+=', '+');
3106 assignop('-=', '-');
3107 assignop('*=', '*');
3108 assignop('/=', '/').nud = function () {
3109 next_token.stop('slash_equal');
3110 };
3111 assignop('%=', '%');
3112 assignop('&=', '&');
3113 assignop('|=', '|');
3114 assignop('^=', '^');
3115 assignop('<<=', '<<');
3116 assignop('>>=', '>>');
3117 assignop('>>>=', '>>>');
3118
3119 function function_parameters() {
3120 var id, parameters = [], paren = next_token;
3121 advance('(');
3122 token.function = funct;
3123 step_in();
3124 no_space();
3125 if (next_token.id !== ')') {
3126 for (;;) {
3127 edge();
3128 id = identifier();
3129 if (token.reserved) {
3130 token.warn('expected_identifier_a_reserved');
3131 }
3132 define('parameter', token);
3133 parameters.push(id);
3134 token.init = true;
3135 token.writeable = true;
3136 if (next_token.id !== ',') {
3137 break;
3138 }
3139 comma();
3140 }
3141 }
3142 no_space();
3143 step_out(')', paren);
3144 return parameters;
3145 }
3146
3147 function do_function(func, name) {
3148 var old_funct = funct,
3149 old_option = option,
3150 old_scope = scope;
3151 scope = Object.create(old_scope);
3152 funct = {
3153 closure: [],
3154 global: [],
3155 level: old_funct.level + 1,
3156 line: next_token.line,
3157 loopage: 0,
3158 name: name || '\'' + (anonname || '').replace(nx, sanitize) + '\'',
3159 outer: [],
3160 scope: scope
3161 };
3162 funct.parameter = function_parameters();
3163 func.function = funct;
3164 option = Object.create(old_option);
3165 functions.push(funct);
3166 if (name) {
3167 func.name = name;
3168 func.string = name;
3169 define('function', func);
3170 func.init = true;
3171 func.used += 1;
3172 }
3173 func.writeable = false;
3174 one_space();
3175 func.block = block('function');
3176 Object.keys(scope).forEach(function (name) {
3177 var master = scope[name];
3178 if (!master.used && master.kind !== 'exception' &&
3179 (master.kind !== 'parameter' || !option.unparam)) {
3180 master.warn('unused_a');
3181 } else if (!master.init) {
3182 master.warn('uninitialized_a');
3183 }
3184 });
3185 funct = old_funct;
3186 option = old_option;
3187 scope = old_scope;
3188 }
3189
3190 prefix('{', function (that) {
3191 var get, i, j, name, set, seen = Object.create(null);
3192 that.first = [];
3193 step_in();
3194 while (next_token.id !== '}') {
3195 indent.wrap = false;
3196
3197 // JSLint recognizes the ES5 extension for get/set in object literals,
3198 // but requires that they be used in pairs.
3199
3200 edge();
3201 if (next_token.string === 'get' && peek().id !== ':') {
3202 get = next_token;
3203 advance('get');
3204 one_space_only();
3205 name = next_token;
3206 i = property_name();
3207 if (!i) {
3208 next_token.stop('missing_property');
3209 }
3210 get.string = '';
3211 do_function(get);
3212 if (funct.loopage) {
3213 get.warn('function_loop');
3214 }
3215 if (get.function.parameter.length) {
3216 get.warn('parameter_a_get_b', get.function.parameter[0], i);
3217 }
3218 comma();
3219 set = next_token;
3220 spaces();
3221 edge();
3222 advance('set');
3223 set.string = '';
3224 one_space_only();
3225 j = property_name();
3226 if (i !== j) {
3227 token.stop('expected_a_b', i, j || next_token.string);
3228 }
3229 do_function(set);
3230 if (set.block.length === 0) {
3231 token.warn('missing_a', 'throw');
3232 }
3233 if (set.function.parameter.length === 0) {
3234 set.stop('parameter_set_a', 'value');
3235 } else if (set.function.parameter[0] !== 'value') {
3236 set.stop('expected_a_b', 'value',
3237 set.function.parameter[0]);
3238 }
3239 name.first = [get, set];
3240 } else {
3241 name = next_token;
3242 i = property_name();
3243 if (typeof i !== 'string') {
3244 next_token.stop('missing_property');
3245 }
3246 advance(':');
3247 spaces();
3248 name.first = expression(10);
3249 }
3250 that.first.push(name);
3251 if (seen[i] === true) {
3252 next_token.warn('duplicate_a', i);
3253 }
3254 seen[i] = true;
3255 tally_property(i);
3256 if (next_token.id !== ',') {
3257 break;
3258 }
3259 for (;;) {
3260 comma();
3261 if (next_token.id !== ',') {
3262 break;
3263 }
3264 next_token.warn('unexpected_a');
3265 }
3266 if (next_token.id === '}') {
3267 token.warn('unexpected_a');
3268 }
3269 }
3270 step_out('}', that);
3271 return that;
3272 });
3273
3274 stmt('{', function () {
3275 next_token.warn('statement_block');
3276 this.arity = 'statement';
3277 this.block = statements();
3278 this.disrupt = this.block.disrupt;
3279 advance('}', this);
3280 return this;
3281 });
3282
3283 stmt('/*global', directive);
3284 stmt('/*globals', directive);
3285 stmt('/*jslint', directive);
3286 stmt('/*member', directive);
3287 stmt('/*members', directive);
3288 stmt('/*property', directive);
3289 stmt('/*properties', directive);
3290
3291 stmt('var', function () {
3292
3293 // JavaScript does not have block scope. It only has function scope. So,
3294 // declaring a variable in a block can have unexpected consequences.
3295
3296 // var.first will contain an array, the array containing name tokens
3297 // and assignment tokens.
3298
3299 var assign, id, name;
3300
3301 if (funct.loopage) {
3302 next_token.warn('var_loop');
3303 } else if (funct.varstatement && !option.vars) {
3304 next_token.warn('combine_var');
3305 }
3306 if (funct !== global_funct) {
3307 funct.varstatement = true;
3308 }
3309 this.arity = 'statement';
3310 this.first = [];
3311 step_in('var');
3312 for (;;) {
3313 name = next_token;
3314 id = identifier(true);
3315 define('var', name);
3316 name.dead = funct;
3317 if (next_token.id === '=') {
3318 if (funct === global_funct && !name.writeable) {
3319 name.warn('read_only');
3320 }
3321 assign = next_token;
3322 assign.first = name;
3323 spaces();
3324 advance('=');
3325 spaces();
3326 if (next_token.id === 'undefined') {
3327 token.warn('unnecessary_initialize', id);
3328 }
3329 if (peek(0).id === '=' && next_token.identifier) {
3330 next_token.stop('var_a_not');
3331 }
3332 assign.second = expression(0);
3333 assign.arity = 'infix';
3334 name.init = true;
3335 this.first.push(assign);
3336 } else {
3337 this.first.push(name);
3338 }
3339 name.dead = false;
3340 name.writeable = true;
3341 if (next_token.id !== ',') {
3342 break;
3343 }
3344 comma();
3345 indent.wrap = false;
3346 if (var_mode && next_token.line === token.line &&
3347 this.first.length === 1) {
3348 var_mode = null;
3349 indent.open = false;
3350 indent.at -= option.indent;
3351 }
3352 spaces();
3353 edge();
3354 }
3355 var_mode = null;
3356 step_out();
3357 return this;
3358 });
3359
3360 stmt('function', function () {
3361 one_space();
3362 if (in_block) {
3363 token.warn('function_block');
3364 }
3365 var name = next_token,
3366 id = identifier(true);
3367 define('var', name);
3368 if (!name.writeable) {
3369 name.warn('read_only');
3370 }
3371 name.init = true;
3372 name.statement = true;
3373 no_space();
3374 this.arity = 'statement';
3375 do_function(this, id);
3376 if (next_token.id === '(' && next_token.line === token.line) {
3377 next_token.stop('function_statement');
3378 }
3379 return this;
3380 });
3381
3382 prefix('function', function (that) {
3383 var id = optional_identifier(true), name;
3384 if (id) {
3385 name = token;
3386 no_space();
3387 } else {
3388 id = '';
3389 one_space();
3390 }
3391 do_function(that, id);
3392 if (name) {
3393 name.function = that.function;
3394 }
3395 if (funct.loopage) {
3396 that.warn('function_loop');
3397 }
3398 switch (next_token.id) {
3399 case ';':
3400 case '(':
3401 case ')':
3402 case ',':
3403 case ']':
3404 case '}':
3405 case ':':
3406 case '(end)':
3407 break;
3408 case '.':
3409 if (peek().string !== 'bind' || peek(1).id !== '(') {
3410 next_token.warn('unexpected_a');
3411 }
3412 break;
3413 default:
3414 next_token.stop('unexpected_a');
3415 }
3416 that.arity = 'function';
3417 return that;
3418 });
3419
3420 stmt('if', function () {
3421 var paren = next_token;
3422 one_space();
3423 advance('(');
3424 step_in('control');
3425 no_space();
3426 edge();
3427 this.arity = 'statement';
3428 this.first = expected_condition(expected_relation(expression(0)));
3429 no_space();
3430 step_out(')', paren);
3431 one_space();
3432 this.block = block('if');
3433 if (next_token.id === 'else') {
3434 if (this.block.disrupt) {
3435 next_token.warn(this.elif ? 'use_nested_if' : 'unnecessary_else');
3436 }
3437 one_space();
3438 advance('else');
3439 one_space();
3440 if (next_token.id === 'if') {
3441 next_token.elif = true;
3442 this.else = statement(true);
3443 } else {
3444 this.else = block('else');
3445 }
3446 if (this.else.disrupt && this.block.disrupt) {
3447 this.disrupt = true;
3448 }
3449 }
3450 return this;
3451 });
3452
3453 stmt('try', function () {
3454
3455 // try.first The catch variable
3456 // try.second The catch clause
3457 // try.third The finally clause
3458 // try.block The try block
3459
3460 var exception_variable, paren;
3461 one_space();
3462 this.arity = 'statement';
3463 this.block = block('try');
3464 if (next_token.id === 'catch') {
3465 one_space();
3466 advance('catch');
3467 one_space();
3468 paren = next_token;
3469 advance('(');
3470 step_in('control');
3471 no_space();
3472 edge();
3473 exception_variable = next_token;
3474 this.first = identifier();
3475 define('exception', exception_variable);
3476 exception_variable.init = true;
3477 no_space();
3478 step_out(')', paren);
3479 one_space();
3480 this.second = block('catch');
3481 if (this.second.length) {
3482 if (this.first === 'ignore') {
3483 exception_variable.warn('unexpected_a');
3484 }
3485 } else {
3486 if (this.first !== 'ignore') {
3487 exception_variable.warn('expected_a_b', 'ignore',
3488 exception_variable.string);
3489 }
3490 }
3491 exception_variable.dead = true;
3492 }
3493 if (next_token.id === 'finally') {
3494 one_space();
3495 advance('finally');
3496 one_space();
3497 this.third = block('finally');
3498 } else if (!this.second) {
3499 next_token.stop('expected_a_b', 'catch', artifact());
3500 }
3501 return this;
3502 });
3503
3504 labeled_stmt('while', function () {
3505 one_space();
3506 var paren = next_token;
3507 funct.loopage += 1;
3508 advance('(');
3509 step_in('control');
3510 no_space();
3511 edge();
3512 this.arity = 'statement';
3513 this.first = expected_relation(expression(0));
3514 if (this.first.id !== 'true') {
3515 expected_condition(this.first, 'unexpected_a');
3516 }
3517 no_space();
3518 step_out(')', paren);
3519 one_space();
3520 this.block = block('while');
3521 if (this.block.disrupt) {
3522 prev_token.warn('strange_loop');
3523 }
3524 funct.loopage -= 1;
3525 return this;
3526 });
3527
3528 reserve('with');
3529
3530 labeled_stmt('switch', function () {
3531
3532 // switch.first the switch expression
3533 // switch.second the array of cases. A case is 'case' or 'default' token:
3534 // case.first the array of case expressions
3535 // case.second the array of statements
3536 // If all of the arrays of statements are disrupt, then the switch is disrupt.
3537
3538 var cases = [],
3539 old_in_block = in_block,
3540 particular,
3541 that = token,
3542 the_case = next_token;
3543
3544 function find_duplicate_case(value) {
3545 if (are_similar(particular, value)) {
3546 value.warn('duplicate_a');
3547 }
3548 }
3549
3550 one_space();
3551 advance('(');
3552 no_space();
3553 step_in();
3554 this.arity = 'statement';
3555 this.first = expected_condition(expected_relation(expression(0)));
3556 no_space();
3557 step_out(')', the_case);
3558 one_space();
3559 advance('{');
3560 step_in();
3561 in_block = true;
3562 this.second = [];
3563 if (that.from !== next_token.from && !option.white) {
3564 next_token.warn('expected_a_at_b_c', next_token.string, that.from, next_token.from);
3565 }
3566 while (next_token.id === 'case') {
3567 the_case = next_token;
3568 the_case.first = [];
3569 the_case.arity = 'case';
3570 for (;;) {
3571 spaces();
3572 edge('case');
3573 advance('case');
3574 one_space();
3575 particular = expression(0);
3576 cases.forEach(find_duplicate_case);
3577 cases.push(particular);
3578 the_case.first.push(particular);
3579 if (particular.id === 'NaN') {
3580 particular.warn('unexpected_a');
3581 }
3582 no_space_only();
3583 advance(':');
3584 if (next_token.id !== 'case') {
3585 break;
3586 }
3587 }
3588 spaces();
3589 the_case.second = statements();
3590 if (the_case.second && the_case.second.length > 0) {
3591 if (!the_case.second[the_case.second.length - 1].disrupt) {
3592 next_token.warn('missing_a_after_b', 'break', 'case');
3593 }
3594 } else {
3595 next_token.warn('empty_case');
3596 }
3597 this.second.push(the_case);
3598 }
3599 if (this.second.length === 0) {
3600 next_token.warn('missing_a', 'case');
3601 }
3602 if (next_token.id === 'default') {
3603 spaces();
3604 the_case = next_token;
3605 the_case.arity = 'case';
3606 edge('case');
3607 advance('default');
3608 no_space_only();
3609 advance(':');
3610 spaces();
3611 the_case.second = statements();
3612 if (the_case.second && the_case.second.length > 0) {
3613 this.disrupt = the_case.second[the_case.second.length - 1].disrupt;
3614 } else {
3615 the_case.warn('empty_case');
3616 }
3617 this.second.push(the_case);
3618 }
3619 if (this.break) {
3620 this.disrupt = false;
3621 }
3622 spaces();
3623 step_out('}', this);
3624 in_block = old_in_block;
3625 return this;
3626 });
3627
3628 stmt('debugger', function () {
3629 if (!option.debug) {
3630 this.warn('unexpected_a');
3631 }
3632 this.arity = 'statement';
3633 return this;
3634 });
3635
3636 labeled_stmt('do', function () {
3637 funct.loopage += 1;
3638 one_space();
3639 this.arity = 'statement';
3640 this.block = block('do');
3641 if (this.block.disrupt) {
3642 prev_token.warn('strange_loop');
3643 }
3644 one_space();
3645 advance('while');
3646 var paren = next_token;
3647 one_space();
3648 advance('(');
3649 step_in();
3650 no_space();
3651 edge();
3652 this.first = expected_condition(expected_relation(expression(0)), 'unexpected_a');
3653 no_space();
3654 step_out(')', paren);
3655 funct.loopage -= 1;
3656 return this;
3657 });
3658
3659 labeled_stmt('for', function () {
3660
3661 var blok, filter, master, ok = false, paren = next_token, value;
3662 this.arity = 'statement';
3663 funct.loopage += 1;
3664 advance('(');
3665 if (next_token.id === ';') {
3666 no_space();
3667 advance(';');
3668 no_space();
3669 advance(';');
3670 no_space();
3671 advance(')');
3672 blok = block('for');
3673 } else {
3674 step_in('control');
3675 spaces(this, paren);
3676 no_space();
3677 if (next_token.id === 'var') {
3678 next_token.stop('move_var');
3679 }
3680 edge();
3681 if (peek(0).id === 'in') {
3682 this.forin = true;
3683 value = expression(1000);
3684 master = value.master;
3685 if (!master) {
3686 value.stop('bad_in_a');
3687 }
3688 if (master.kind !== 'var' || master.function !== funct ||
3689 !master.writeable || master.dead) {
3690 value.warn('bad_in_a');
3691 }
3692 master.init = true;
3693 master.used -= 1;
3694 this.first = value;
3695 advance('in');
3696 this.second = expression(20);
3697 step_out(')', paren);
3698 blok = block('for');
3699 if (!option.forin) {
3700 if (blok.length === 1 && typeof blok[0] === 'object') {
3701 if (blok[0].id === 'if' && !blok[0].else) {
3702 filter = blok[0].first;
3703 while (filter.id === '&&') {
3704 filter = filter.first;
3705 }
3706 switch (filter.id) {
3707 case '===':
3708 case '!==':
3709 ok = filter.first.id === '['
3710 ? are_similar(filter.first.first, this.second) &&
3711 are_similar(filter.first.second, this.first)
3712 : filter.first.id === 'typeof' &&
3713 filter.first.first.id === '[' &&
3714 are_similar(filter.first.first.first, this.second) &&
3715 are_similar(filter.first.first.second, this.first);
3716 break;
3717 case '(':
3718 ok = filter.first.id === '.' && ((
3719 are_similar(filter.first.first, this.second) &&
3720 filter.first.second.string === 'hasOwnProperty' &&
3721 are_similar(filter.second[0], this.first)
3722 ) || (
3723 filter.first.first.id === '.' &&
3724 filter.first.first.first.first &&
3725 filter.first.first.first.first.string === 'Object' &&
3726 filter.first.first.first.id === '.' &&
3727 filter.first.first.first.second.string === 'prototype' &&
3728 filter.first.first.second.string === 'hasOwnProperty' &&
3729 filter.first.second.string === 'call' &&
3730 are_similar(filter.second[0], this.second) &&
3731 are_similar(filter.second[1], this.first)
3732 ));
3733 break;
3734 }
3735 } else if (blok[0].id === 'switch') {
3736 ok = blok[0].id === 'switch' &&
3737 blok[0].first.id === 'typeof' &&
3738 blok[0].first.first.id === '[' &&
3739 are_similar(blok[0].first.first.first, this.second) &&
3740 are_similar(blok[0].first.first.second, this.first);
3741 }
3742 }
3743 if (!ok) {
3744 this.warn('for_if');
3745 }
3746 }
3747 } else {
3748 edge();
3749 this.first = [];
3750 for (;;) {
3751 this.first.push(expression(0, 'for'));
3752 if (next_token.id !== ',') {
3753 break;
3754 }
3755 comma();
3756 }
3757 semicolon();
3758 edge();
3759 this.second = expected_relation(expression(0));
3760 if (this.second.id !== 'true') {
3761 expected_condition(this.second, 'unexpected_a');
3762 }
3763 semicolon(token);
3764 if (next_token.id === ';') {
3765 next_token.stop('expected_a_b', ')', ';');
3766 }
3767 this.third = [];
3768 edge();
3769 for (;;) {
3770 this.third.push(expression(0, 'for'));
3771 if (next_token.id !== ',') {
3772 break;
3773 }
3774 comma();
3775 }
3776 no_space();
3777 step_out(')', paren);
3778 one_space();
3779 blok = block('for');
3780 }
3781 }
3782 if (blok.disrupt) {
3783 prev_token.warn('strange_loop');
3784 }
3785 this.block = blok;
3786 funct.loopage -= 1;
3787 return this;
3788 });
3789
3790 function optional_label(that) {
3791 var label = next_token.string,
3792 master;
3793 that.arity = 'statement';
3794 if (!funct.breakage || (!option.continue && that.id === 'continue')) {
3795 that.warn('unexpected_a');
3796 } else if (next_token.identifier && token.line === next_token.line) {
3797 one_space_only();
3798 master = scope[label];
3799 if (!master || master.kind !== 'label') {
3800 next_token.warn('not_a_label');
3801 } else if (master.dead || master.function !== funct) {
3802 next_token.warn('not_a_scope');
3803 } else {
3804 master.used += 1;
3805 if (that.id === 'break') {
3806 master.statement.break = true;
3807 }
3808 if (funct.breakage[funct.breakage.length - 1] === master.statement) {
3809 next_token.warn('unexpected_a');
3810 }
3811 }
3812 that.first = next_token;
3813 advance();
3814 } else {
3815 if (that.id === 'break') {
3816 funct.breakage[funct.breakage.length - 1].break = true;
3817 }
3818 }
3819 return that;
3820
3821 }
3822
3823 disrupt_stmt('break', function () {
3824 return optional_label(this);
3825 });
3826
3827 disrupt_stmt('continue', function () {
3828 return optional_label(this);
3829 });
3830
3831 disrupt_stmt('return', function () {
3832 if (funct === global_funct) {
3833 this.warn('unexpected_a');
3834 }
3835 this.arity = 'statement';
3836 if (next_token.id !== ';' && next_token.line === token.line) {
3837 if (option.closure) {
3838 spaces();
3839 } else {
3840 one_space_only();
3841 }
3842 if (next_token.id === '/' || next_token.id === '(regexp)') {
3843 next_token.warn('wrap_regexp');
3844 }
3845 this.first = expression(0);
3846 if (this.first.assign) {
3847 this.first.warn('unexpected_a');
3848 }
3849 }
3850 return this;
3851 });
3852
3853 disrupt_stmt('throw', function () {
3854 this.arity = 'statement';
3855 one_space_only();
3856 this.first = expression(20);
3857 return this;
3858 });
3859
3860
3861 // Superfluous reserved words
3862
3863 reserve('class');
3864 reserve('const');
3865 reserve('enum');
3866 reserve('export');
3867 reserve('extends');
3868 reserve('import');
3869 reserve('super');
3870
3871 // Harmony reserved words
3872
3873 reserve('implements');
3874 reserve('interface');
3875 reserve('let');
3876 reserve('package');
3877 reserve('private');
3878 reserve('protected');
3879 reserve('public');
3880 reserve('static');
3881 reserve('yield');
3882
3883
3884 // Parse JSON
3885
3886 function json_value() {
3887
3888 function json_object() {
3889 var brace = next_token, object = Object.create(null);
3890 advance('{');
3891 if (next_token.id !== '}') {
3892 while (next_token.id !== '(end)') {
3893 while (next_token.id === ',') {
3894 next_token.warn('unexpected_a');
3895 advance(',');
3896 }
3897 if (next_token.id !== '(string)') {
3898 next_token.warn('expected_string_a');
3899 }
3900 if (object[next_token.string] === true) {
3901 next_token.warn('duplicate_a');
3902 } else if (next_token.string === '__proto__') {
3903 next_token.warn('dangling_a');
3904 } else {
3905 object[next_token.string] = true;
3906 }
3907 advance();
3908 advance(':');
3909 json_value();
3910 if (next_token.id !== ',') {
3911 break;
3912 }
3913 advance(',');
3914 if (next_token.id === '}') {
3915 token.warn('unexpected_a');
3916 break;
3917 }
3918 }
3919 }
3920 advance('}', brace);
3921 }
3922
3923 function json_array() {
3924 var bracket = next_token;
3925 advance('[');
3926 if (next_token.id !== ']') {
3927 while (next_token.id !== '(end)') {
3928 while (next_token.id === ',') {
3929 next_token.warn('unexpected_a');
3930 advance(',');
3931 }
3932 json_value();
3933 if (next_token.id !== ',') {
3934 break;
3935 }
3936 advance(',');
3937 if (next_token.id === ']') {
3938 token.warn('unexpected_a');
3939 break;
3940 }
3941 }
3942 }
3943 advance(']', bracket);
3944 }
3945
3946 switch (next_token.id) {
3947 case '{':
3948 json_object();
3949 break;
3950 case '[':
3951 json_array();
3952 break;
3953 case 'true':
3954 case 'false':
3955 case 'null':
3956 case '(number)':
3957 case '(string)':
3958 advance();
3959 break;
3960 case '-':
3961 advance('-');
3962 no_space_only();
3963 advance('(number)');
3964 break;
3965 default:
3966 next_token.stop('unexpected_a');
3967 }
3968 }
3969
3970
3971 // The actual JSLINT function itself.
3972
3973 itself = function JSLint(the_source, the_option) {
3974
3975 var i, predef, tree;
3976 itself.errors = [];
3977 itself.tree = '';
3978 itself.properties = '';
3979 begin = prev_token = token = next_token =
3980 Object.create(syntax['(begin)']);
3981 tokens = [];
3982 predefined = Object.create(null);
3983 add_to_predefined(standard);
3984 property = Object.create(null);
3985 if (the_option) {
3986 option = Object.create(the_option);
3987 predef = option.predef;
3988 if (predef) {
3989 if (Array.isArray(predef)) {
3990 for (i = 0; i < predef.length; i += 1) {
3991 predefined[predef[i]] = true;
3992 }
3993 } else if (typeof predef === 'object') {
3994 add_to_predefined(predef);
3995 }
3996 }
3997 } else {
3998 option = Object.create(null);
3999 }
4000 option.indent = +option.indent || 4;
4001 option.maxerr = +option.maxerr || 50;
4002 global_scope = scope = Object.create(null);
4003 global_funct = funct = {
4004 scope: scope,
4005 loopage: 0,
4006 level: 0
4007 };
4008 functions = [funct];
4009 block_var = [];
4010
4011 comments = [];
4012 comments_off = false;
4013 in_block = false;
4014 indent = null;
4015 json_mode = false;
4016 lookahead = [];
4017 node_js = false;
4018 prereg = true;
4019 strict_mode = false;
4020 var_mode = null;
4021 warnings = 0;
4022 lex.init(the_source);
4023
4024 assume();
4025
4026 try {
4027 advance();
4028 if (next_token.id === '(number)') {
4029 next_token.stop('unexpected_a');
4030 } else {
4031 switch (next_token.id) {
4032 case '{':
4033 case '[':
4034 comments_off = true;
4035 json_mode = true;
4036 json_value();
4037 break;
4038 default:
4039
4040 // If the first token is a semicolon, ignore it. This is sometimes used when
4041 // files are intended to be appended to files that may be sloppy. A sloppy
4042 // file may be depending on semicolon insertion on its last line.
4043
4044 step_in(1);
4045 if (next_token.id === ';' && !node_js) {
4046 next_token.edge = true;
4047 advance(';');
4048 }
4049 tree = statements();
4050 begin.first = tree;
4051 itself.tree = begin;
4052 if (tree.disrupt) {
4053 prev_token.warn('weird_program');
4054 }
4055 }
4056 }
4057 indent = null;
4058 advance('(end)');
4059 itself.property = property;
4060 } catch (e) {
4061 if (e) { // ~~
4062 itself.errors.push({
4063 reason : e.message,
4064 line : e.line || next_token.line,
4065 character : e.character || next_token.from
4066 }, null);
4067 }
4068 }
4069 return itself.errors.length === 0;
4070 };
4071
4072 function unique(array) {
4073 array = array.sort();
4074 var i, length = 0, previous, value;
4075 for (i = 0; i < array.length; i += 1) {
4076 value = array[i];
4077 if (value !== previous) {
4078 array[length] = value;
4079 previous = value;
4080 length += 1;
4081 }
4082 }
4083 array.length = length;
4084 return array;
4085 }
4086
4087 // Data summary.
4088
4089 itself.data = function () {
4090 var data = {functions: []},
4091 function_data,
4092 i,
4093 the_function,
4094 the_scope;
4095 data.errors = itself.errors;
4096 data.json = json_mode;
4097 data.global = unique(Object.keys(global_scope));
4098
4099 function selects(name) {
4100 var kind = the_scope[name].kind;
4101 switch (kind) {
4102 case 'var':
4103 case 'exception':
4104 case 'label':
4105 function_data[kind].push(name);
4106 break;
4107 }
4108 }
4109
4110 for (i = 1; i < functions.length; i += 1) {
4111 the_function = functions[i];
4112 function_data = {
4113 name: the_function.name,
4114 line: the_function.line,
4115 level: the_function.level,
4116 parameter: the_function.parameter,
4117 var: [],
4118 exception: [],
4119 closure: unique(the_function.closure),
4120 outer: unique(the_function.outer),
4121 global: unique(the_function.global),
4122 label: []
4123 };
4124 the_scope = the_function.scope;
4125 Object.keys(the_scope).forEach(selects);
4126 function_data.var.sort();
4127 function_data.exception.sort();
4128 function_data.label.sort();
4129 data.functions.push(function_data);
4130 }
4131 data.tokens = tokens;
4132 return data;
4133 };
4134
4135 itself.error_report = function (data) {
4136 var evidence, i, output = [], warning;
4137 if (data.errors.length) {
4138 if (data.json) {
4139 output.push('<cite>JSON: bad.</cite><br>');
4140 }
4141 for (i = 0; i < data.errors.length; i += 1) {
4142 warning = data.errors[i];
4143 if (warning) {
4144 evidence = warning.evidence || '';
4145 output.push('<cite>');
4146 if (isFinite(warning.line)) {
4147 output.push('<address>line ' +
4148 String(warning.line) +
4149 ' character ' + String(warning.character) +
4150 '</address>');
4151 }
4152 output.push(warning.reason.entityify() + '</cite>');
4153 if (evidence) {
4154 output.push('<pre>' + evidence.entityify() + '</pre>');
4155 }
4156 }
4157 }
4158 }
4159 return output.join('');
4160 };
4161
4162
4163 itself.report = function (data) {
4164 var dl, i, j, names, output = [], the_function;
4165
4166 function detail(h, array) {
4167 var comma_needed = false;
4168 if (array.length) {
4169 output.push("<dt>" + h + "</dt><dd>");
4170 array.forEach(function (item) {
4171 output.push((comma_needed ? ', ' : '') + item);
4172 comma_needed = true;
4173 });
4174 output.push("</dd>");
4175 }
4176 }
4177
4178 output.push('<dl class=level0>');
4179 if (data.global.length) {
4180 detail('global', data.global);
4181 dl = true;
4182 } else if (data.json) {
4183 if (!data.errors.length) {
4184 output.push("<dt>JSON: good.</dt>");
4185 }
4186 } else {
4187 output.push("<dt><i>No new global variables introduced.</i></dt>");
4188 }
4189 if (dl) {
4190 output.push("</dl>");
4191 } else {
4192 output[0] = '';
4193 }
4194
4195 if (data.functions) {
4196 for (i = 0; i < data.functions.length; i += 1) {
4197 the_function = data.functions[i];
4198 names = [];
4199 if (the_function.params) {
4200 for (j = 0; j < the_function.params.length; j += 1) {
4201 names[j] = the_function.params[j].string;
4202 }
4203 }
4204 output.push('<dl class=level' + the_function.level +
4205 '><address>line ' + String(the_function.line) +
4206 '</address>' + the_function.name.entityify());
4207 detail('parameter', the_function.parameter);
4208 detail('variable', the_function.var);
4209 detail('exception', the_function.exception);
4210 detail('closure', the_function.closure);
4211 detail('outer', the_function.outer);
4212 detail('global', the_function.global);
4213 detail('label', the_function.label);
4214 output.push('</dl>');
4215 }
4216 }
4217 return output.join('');
4218 };
4219
4220 itself.properties_report = function (property) {
4221 if (!property) {
4222 return '';
4223 }
4224 var i,
4225 key,
4226 keys = Object.keys(property).sort(),
4227 mem = ' ',
4228 name,
4229 not_first = false,
4230 output = ['/*properties'];
4231 for (i = 0; i < keys.length; i += 1) {
4232 key = keys[i];
4233 if (property[key] > 0) {
4234 if (not_first) {
4235 mem += ',';
4236 }
4237 name = ix.test(key)
4238 ? key
4239 : '\'' + key.replace(nx, sanitize) + '\'';
4240 if (mem.length + name.length >= 80) {
4241 output.push(mem);
4242 mem = ' ';
4243 } else {
4244 mem += ' ';
4245 }
4246 mem += name;
4247 not_first = true;
4248 }
4249 }
4250 output.push(mem, '*/\n');
4251 return output.join('\n');
4252 };
4253
4254 itself.color = function (data) {
4255 var from,
4256 i = 1,
4257 level,
4258 line,
4259 result = [],
4260 thru,
4261 data_token = data.tokens[0];
4262 while (data_token && data_token.id !== '(end)') {
4263 from = data_token.from;
4264 line = data_token.line;
4265 thru = data_token.thru;
4266 level = data_token.function.level;
4267 do {
4268 thru = data_token.thru;
4269 data_token = data.tokens[i];
4270 i += 1;
4271 } while (data_token && data_token.line === line &&
4272 data_token.from - thru < 5 &&
4273 level === data_token.function.level);
4274 result.push({
4275 line: line,
4276 level: level,
4277 from: from,
4278 thru: thru
4279 });
4280 }
4281 return result;
4282 };
4283
4284 itself.jslint = itself;
4285
4286 itself.edition = '2014-07-08';
4287
4288 return itself;
4289 }());