mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-09-28 13:13:37 +08:00
We never install the packages mentioned in that package json but rely on the node_modules one, so removed. Also update the packages and types Bug: none Change-Id: I0676c36621f39bff7408c5b910f5e4461f3f0158 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6394480 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Auto-Submit: Nikolay Vitkov <nvitkov@chromium.org> Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
345 lines
7.0 KiB
JavaScript
345 lines
7.0 KiB
JavaScript
import { syntaxError } from "./error.js";
|
|
import { unescape } from "./productions/helpers.js";
|
|
|
|
// These regular expressions use the sticky flag so they will only match at
|
|
// the current location (ie. the offset of lastIndex).
|
|
const tokenRe = {
|
|
// This expression uses a lookahead assertion to catch false matches
|
|
// against integers early.
|
|
decimal:
|
|
/-?(?=[0-9]*\.|[0-9]+[eE])(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/y,
|
|
integer: /-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/y,
|
|
identifier: /[_-]?[A-Za-z][0-9A-Z_a-z-]*/y,
|
|
string: /"[^"]*"/y,
|
|
whitespace: /[\t\n\r ]+/y,
|
|
comment: /\/\/.*|\/\*[\s\S]*?\*\//y,
|
|
other: /[^\t\n\r 0-9A-Za-z]/y,
|
|
};
|
|
|
|
export const typeNameKeywords = [
|
|
"ArrayBuffer",
|
|
"SharedArrayBuffer",
|
|
"DataView",
|
|
"Int8Array",
|
|
"Int16Array",
|
|
"Int32Array",
|
|
"Uint8Array",
|
|
"Uint16Array",
|
|
"Uint32Array",
|
|
"Uint8ClampedArray",
|
|
"BigInt64Array",
|
|
"BigUint64Array",
|
|
"Float32Array",
|
|
"Float64Array",
|
|
"any",
|
|
"object",
|
|
"symbol",
|
|
];
|
|
|
|
export const stringTypes = ["ByteString", "DOMString", "USVString"];
|
|
|
|
export const argumentNameKeywords = [
|
|
"async",
|
|
"attribute",
|
|
"callback",
|
|
"const",
|
|
"constructor",
|
|
"deleter",
|
|
"dictionary",
|
|
"enum",
|
|
"getter",
|
|
"includes",
|
|
"inherit",
|
|
"interface",
|
|
"iterable",
|
|
"maplike",
|
|
"namespace",
|
|
"partial",
|
|
"required",
|
|
"setlike",
|
|
"setter",
|
|
"static",
|
|
"stringifier",
|
|
"typedef",
|
|
"unrestricted",
|
|
];
|
|
|
|
const nonRegexTerminals = [
|
|
"-Infinity",
|
|
"FrozenArray",
|
|
"Infinity",
|
|
"NaN",
|
|
"ObservableArray",
|
|
"Promise",
|
|
"bigint",
|
|
"boolean",
|
|
"byte",
|
|
"double",
|
|
"false",
|
|
"float",
|
|
"long",
|
|
"mixin",
|
|
"null",
|
|
"octet",
|
|
"optional",
|
|
"or",
|
|
"readonly",
|
|
"record",
|
|
"sequence",
|
|
"short",
|
|
"true",
|
|
"undefined",
|
|
"unsigned",
|
|
"void",
|
|
].concat(argumentNameKeywords, stringTypes, typeNameKeywords);
|
|
|
|
const punctuations = [
|
|
"(",
|
|
")",
|
|
",",
|
|
"...",
|
|
":",
|
|
";",
|
|
"<",
|
|
"=",
|
|
">",
|
|
"?",
|
|
"*",
|
|
"[",
|
|
"]",
|
|
"{",
|
|
"}",
|
|
];
|
|
|
|
const reserved = [
|
|
// "constructor" is now a keyword
|
|
"_constructor",
|
|
"toString",
|
|
"_toString",
|
|
];
|
|
|
|
/**
|
|
* @typedef {ArrayItemType<ReturnType<typeof tokenise>>} Token
|
|
* @param {string} str
|
|
*/
|
|
function tokenise(str) {
|
|
const tokens = [];
|
|
let lastCharIndex = 0;
|
|
let trivia = "";
|
|
let line = 1;
|
|
let index = 0;
|
|
while (lastCharIndex < str.length) {
|
|
const nextChar = str.charAt(lastCharIndex);
|
|
let result = -1;
|
|
|
|
if (/[\t\n\r ]/.test(nextChar)) {
|
|
result = attemptTokenMatch("whitespace", { noFlushTrivia: true });
|
|
} else if (nextChar === "/") {
|
|
result = attemptTokenMatch("comment", { noFlushTrivia: true });
|
|
}
|
|
|
|
if (result !== -1) {
|
|
const currentTrivia = tokens.pop().value;
|
|
line += (currentTrivia.match(/\n/g) || []).length;
|
|
trivia += currentTrivia;
|
|
index -= 1;
|
|
} else if (/[-0-9.A-Z_a-z]/.test(nextChar)) {
|
|
result = attemptTokenMatch("decimal");
|
|
if (result === -1) {
|
|
result = attemptTokenMatch("integer");
|
|
}
|
|
if (result === -1) {
|
|
result = attemptTokenMatch("identifier");
|
|
const lastIndex = tokens.length - 1;
|
|
const token = tokens[lastIndex];
|
|
if (result !== -1) {
|
|
if (reserved.includes(token.value)) {
|
|
const message = `${unescape(
|
|
token.value
|
|
)} is a reserved identifier and must not be used.`;
|
|
throw new WebIDLParseError(
|
|
syntaxError(tokens, lastIndex, null, message)
|
|
);
|
|
} else if (nonRegexTerminals.includes(token.value)) {
|
|
token.type = "inline";
|
|
}
|
|
}
|
|
}
|
|
} else if (nextChar === '"') {
|
|
result = attemptTokenMatch("string");
|
|
}
|
|
|
|
for (const punctuation of punctuations) {
|
|
if (str.startsWith(punctuation, lastCharIndex)) {
|
|
tokens.push({
|
|
type: "inline",
|
|
value: punctuation,
|
|
trivia,
|
|
line,
|
|
index,
|
|
});
|
|
trivia = "";
|
|
lastCharIndex += punctuation.length;
|
|
result = lastCharIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// other as the last try
|
|
if (result === -1) {
|
|
result = attemptTokenMatch("other");
|
|
}
|
|
if (result === -1) {
|
|
throw new Error("Token stream not progressing");
|
|
}
|
|
lastCharIndex = result;
|
|
index += 1;
|
|
}
|
|
|
|
// remaining trivia as eof
|
|
tokens.push({
|
|
type: "eof",
|
|
value: "",
|
|
trivia,
|
|
line,
|
|
index,
|
|
});
|
|
|
|
return tokens;
|
|
|
|
/**
|
|
* @param {keyof typeof tokenRe} type
|
|
* @param {object} options
|
|
* @param {boolean} [options.noFlushTrivia]
|
|
*/
|
|
function attemptTokenMatch(type, { noFlushTrivia } = {}) {
|
|
const re = tokenRe[type];
|
|
re.lastIndex = lastCharIndex;
|
|
const result = re.exec(str);
|
|
if (result) {
|
|
tokens.push({ type, value: result[0], trivia, line, index });
|
|
if (!noFlushTrivia) {
|
|
trivia = "";
|
|
}
|
|
return re.lastIndex;
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
export class Tokeniser {
|
|
/**
|
|
* @param {string} idl
|
|
*/
|
|
constructor(idl) {
|
|
this.source = tokenise(idl);
|
|
this.position = 0;
|
|
}
|
|
|
|
/**
|
|
* @param {string} message
|
|
* @return {never}
|
|
*/
|
|
error(message) {
|
|
throw new WebIDLParseError(
|
|
syntaxError(this.source, this.position, this.current, message)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {string} type
|
|
*/
|
|
probeKind(type) {
|
|
return (
|
|
this.source.length > this.position &&
|
|
this.source[this.position].type === type
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
*/
|
|
probe(value) {
|
|
return (
|
|
this.probeKind("inline") && this.source[this.position].value === value
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param {...string} candidates
|
|
*/
|
|
consumeKind(...candidates) {
|
|
for (const type of candidates) {
|
|
if (!this.probeKind(type)) continue;
|
|
const token = this.source[this.position];
|
|
this.position++;
|
|
return token;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {...string} candidates
|
|
*/
|
|
consume(...candidates) {
|
|
if (!this.probeKind("inline")) return;
|
|
const token = this.source[this.position];
|
|
for (const value of candidates) {
|
|
if (token.value !== value) continue;
|
|
this.position++;
|
|
return token;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {string} value
|
|
*/
|
|
consumeIdentifier(value) {
|
|
if (!this.probeKind("identifier")) {
|
|
return;
|
|
}
|
|
if (this.source[this.position].value !== value) {
|
|
return;
|
|
}
|
|
return this.consumeKind("identifier");
|
|
}
|
|
|
|
/**
|
|
* @param {number} position
|
|
*/
|
|
unconsume(position) {
|
|
this.position = position;
|
|
}
|
|
}
|
|
|
|
export class WebIDLParseError extends Error {
|
|
/**
|
|
* @param {object} options
|
|
* @param {string} options.message
|
|
* @param {string} options.bareMessage
|
|
* @param {string} options.context
|
|
* @param {number} options.line
|
|
* @param {*} options.sourceName
|
|
* @param {string} options.input
|
|
* @param {*[]} options.tokens
|
|
*/
|
|
constructor({
|
|
message,
|
|
bareMessage,
|
|
context,
|
|
line,
|
|
sourceName,
|
|
input,
|
|
tokens,
|
|
}) {
|
|
super(message);
|
|
|
|
this.name = "WebIDLParseError"; // not to be mangled
|
|
this.bareMessage = bareMessage;
|
|
this.context = context;
|
|
this.line = line;
|
|
this.sourceName = sourceName;
|
|
this.input = input;
|
|
this.tokens = tokens;
|
|
}
|
|
}
|