in web/jslint/src/main/resources/data/jslint-2020-03-28.js [1115:1358]
function lex() {
var array;
var i = 0;
var j = 0;
var last;
var result;
var the_token; // This should properly be a tail recursive function, but sadly, conformant
// implementations of ES6 are still rare. This is the ideal code:
// if (!source_line) {
// source_line = next_line();
// from = 0;
// return (
// source_line === undefined
// ? (
// mega_mode
// ? stop_at("unclosed_mega", mega_line, mega_from)
// : make("(end)")
// )
// : lex()
// );
// }
// Unfortunately, incompetent JavaScript engines will sometimes fail to execute
// it correctly. So for now, we do it the old fashioned way.
while (!source_line) {
source_line = next_line();
from = 0;
if (source_line === undefined) {
return mega_mode ? stop_at("unclosed_mega", mega_line, mega_from) : make("(end)");
}
}
from = column;
result = source_line.match(rx_token); // result[1] token
// result[2] whitespace
// result[3] identifier
// result[4] number
// result[5] rest
if (!result) {
return stop_at("unexpected_char_a", line, column, source_line[0]);
}
snippet = result[1];
column += snippet.length;
source_line = result[5]; // Whitespace was matched. Call lex again to get more.
if (result[2]) {
return lex();
} // The token is an identifier.
if (result[3]) {
return make(snippet, undefined, true);
} // The token is a number.
if (result[4]) {
return number(snippet);
} // The token is a string.
if (snippet === "\"") {
return string(snippet);
}
if (snippet === "'") {
if (!option.single) {
warn_at("use_double", line, column);
}
return string(snippet);
} // The token is a megastring. We don't allow any kind of mega nesting.
if (snippet === "`") {
if (mega_mode) {
return stop_at("expected_a_b", line, column, "}", "`");
}
snippet = "";
mega_from = from;
mega_line = line;
mega_mode = true; // Parsing a mega literal is tricky. First make a ` token.
make("`");
from += 1; // Then loop, building up a string, possibly from many lines, until seeing
// the end of file, a closing `, or a ${ indicting an expression within the
// string.
(function part() {
var at = source_line.search(rx_mega); // If neither ` nor ${ is seen, then the whole line joins the snippet.
if (at < 0) {
snippet += source_line + "\n";
return next_line() === undefined ? stop_at("unclosed_mega", mega_line, mega_from) : part();
} // if either ` or ${ was found, then the preceding joins the snippet to become
// a string token.
snippet += source_line.slice(0, at);
column += at;
source_line = source_line.slice(at);
if (source_line[0] === "\\") {
stop_at("escape_mega", line, at);
}
make("(string)", snippet).quote = "`";
snippet = ""; // If ${, then make tokens that will become part of an expression until
// a } token is made.
if (source_line[0] === "$") {
column += 2;
make("${");
source_line = source_line.slice(2);
(function expr() {
var id = lex().id;
if (id === "{") {
return stop_at("expected_a_b", line, column, "}", "{");
}
if (id !== "}") {
return expr();
}
})();
return part();
}
})();
source_line = source_line.slice(1);
column += 1;
mega_mode = false;
return make("`");
} // The token is a // comment.
if (snippet === "//") {
snippet = source_line;
source_line = "";
the_token = comment(snippet);
if (mega_mode) {
warn("unexpected_comment", the_token, "`");
}
return the_token;
} // The token is a /* comment.
if (snippet === "/*") {
array = [];
if (source_line[0] === "/") {
warn_at("unexpected_a", line, column + i, "/");
}
(function next() {
if (source_line > "") {
i = source_line.search(rx_star_slash);
if (i >= 0) {
return;
}
j = source_line.search(rx_slash_star);
if (j >= 0) {
warn_at("nested_comment", line, column + j);
}
}
array.push(source_line);
source_line = next_line();
if (source_line === undefined) {
return stop_at("unclosed_comment", line, column);
}
return next();
})();
snippet = source_line.slice(0, i);
j = snippet.search(rx_slash_star_or_slash);
if (j >= 0) {
warn_at("nested_comment", line, column + j);
}
array.push(snippet);
column += i + 2;
source_line = source_line.slice(i + 2);
return comment(array);
} // The token is a slash.
if (snippet === "/") {
// The / can be a division operator or the beginning of a regular expression
// literal. It is not possible to know which without doing a complete parse.
// We want to complete the tokenization before we begin to parse, so we will
// estimate. This estimator can fail in some cases. For example, it cannot
// know if "}" is ending a block or ending an object literal, so it can
// behave incorrectly in that case; it is not meaningful to divide an
// object, so it is likely that we can get away with it. We avoided the worst
// cases by eliminating automatic semicolon insertion.
if (prior.identifier) {
if (!prior.dot) {
if (prior.id === "return") {
return regexp();
}
if (prior.id === "(begin)" || prior.id === "case" || prior.id === "delete" || prior.id === "in" || prior.id === "instanceof" || prior.id === "new" || prior.id === "typeof" || prior.id === "void" || prior.id === "yield") {
the_token = regexp();
return stop("unexpected_a", the_token);
}
}
} else {
last = prior.id[prior.id.length - 1];
if ("(,=:?[".indexOf(last) >= 0) {
return regexp();
}
if ("!&|{};~+-*%/^<>".indexOf(last) >= 0) {
the_token = regexp();
warn("wrap_regexp", the_token);
return the_token;
}
}
if (source_line[0] === "/") {
column += 1;
source_line = source_line.slice(1);
snippet = "/=";
warn_at("unexpected_a", line, column, "/=");
}
}
return make(snippet);
}