2024-12-04 02:59:39 +01:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
// @ts-check
|
|
|
|
|
|
|
|
import exp from "constants";
|
2024-12-04 02:59:39 +01:00
|
|
|
import { core } from "ext:core/mod.js";
|
2024-12-06 23:26:11 +01:00
|
|
|
import { deno } from "../tsc/dts/typescript.d.ts";
|
2024-12-04 02:59:39 +01:00
|
|
|
const {
|
2024-12-03 13:45:31 +01:00
|
|
|
op_lint_get_rule,
|
|
|
|
op_lint_get_source,
|
|
|
|
op_lint_report,
|
2024-12-04 02:59:39 +01:00
|
|
|
} = core.ops;
|
2024-12-01 04:53:47 +01:00
|
|
|
|
2024-12-04 12:42:55 +01:00
|
|
|
/** @typedef {{ plugins: Array<{ name: string, rules: Record<string, Deno.LintRule}> }} LintState */
|
|
|
|
|
|
|
|
/** @type {LintState} */
|
2024-12-04 03:29:05 +01:00
|
|
|
const state = {
|
2024-12-04 12:42:55 +01:00
|
|
|
plugins: [],
|
2024-12-04 03:29:05 +01:00
|
|
|
};
|
|
|
|
|
2024-12-01 04:53:47 +01:00
|
|
|
export class Context {
|
|
|
|
id;
|
|
|
|
|
|
|
|
fileName;
|
|
|
|
|
2024-12-04 12:42:55 +01:00
|
|
|
#source = null;
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {string} id
|
|
|
|
* @param {string} fileName
|
|
|
|
*/
|
2024-12-01 04:53:47 +01:00
|
|
|
constructor(id, fileName) {
|
|
|
|
this.id = id;
|
|
|
|
this.fileName = fileName;
|
|
|
|
}
|
|
|
|
|
2024-12-03 13:45:31 +01:00
|
|
|
source() {
|
2024-12-04 12:42:55 +01:00
|
|
|
if (this.#source === null) {
|
|
|
|
this.#source = op_lint_get_source();
|
|
|
|
}
|
|
|
|
return this.#source;
|
2024-12-03 13:45:31 +01:00
|
|
|
}
|
|
|
|
|
2024-12-02 02:27:30 +01:00
|
|
|
report(data) {
|
2024-12-03 03:11:26 +01:00
|
|
|
let start, end;
|
|
|
|
|
|
|
|
if (data.node) {
|
|
|
|
start = data.node.span.start - 1;
|
|
|
|
end = data.node.span.end - 1;
|
|
|
|
} else if (data.span) {
|
|
|
|
start = data.span.start - 1;
|
|
|
|
end = data.span.end - 1;
|
|
|
|
} else {
|
|
|
|
throw new Error(
|
|
|
|
"Either `node` or `span` must be provided when reporting an error",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-12-02 18:29:30 +01:00
|
|
|
op_lint_report(
|
|
|
|
this.id,
|
|
|
|
this.fileName,
|
|
|
|
data.message,
|
2024-12-03 03:11:26 +01:00
|
|
|
start,
|
|
|
|
end,
|
2024-12-02 18:29:30 +01:00
|
|
|
);
|
2024-12-01 04:53:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-04 04:30:32 +01:00
|
|
|
export function installPlugin(plugin) {
|
|
|
|
console.log("plugin", plugin);
|
|
|
|
if (typeof plugin !== "object") {
|
|
|
|
throw new Error("Linter plugin must be an object");
|
|
|
|
}
|
|
|
|
if (typeof plugin.name !== "string") {
|
|
|
|
throw new Error("Linter plugin name must be a string");
|
|
|
|
}
|
|
|
|
if (typeof plugin.rules !== "object") {
|
|
|
|
throw new Error("Linter plugin rules must be an object");
|
|
|
|
}
|
|
|
|
if (typeof state.plugins[plugin.name] !== "undefined") {
|
|
|
|
throw new Error(`Linter plugin ${plugin.name} has already been registered`);
|
|
|
|
}
|
|
|
|
state.plugins[plugin.name] = plugin.rules;
|
|
|
|
console.log("Installed plugin", plugin.name, plugin.rules);
|
2024-12-04 03:29:05 +01:00
|
|
|
}
|
|
|
|
|
2024-12-05 01:18:08 +01:00
|
|
|
// Keep in sync with Rust
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @enum {number}
|
|
|
|
*/
|
|
|
|
const AstNodeId = {
|
2024-12-05 01:18:08 +01:00
|
|
|
Invalid: 0,
|
|
|
|
Program: 1,
|
|
|
|
|
|
|
|
Import: 2,
|
|
|
|
|
|
|
|
// Decls
|
|
|
|
Class: 12,
|
|
|
|
Fn: 13,
|
|
|
|
Var: 14,
|
|
|
|
|
|
|
|
// Statements
|
2024-12-06 23:26:11 +01:00
|
|
|
BlockStatement: 20,
|
2024-12-05 01:18:08 +01:00
|
|
|
Empty: 21,
|
2024-12-06 23:26:11 +01:00
|
|
|
DebuggerStatement: 22,
|
|
|
|
WithStatement: 23,
|
|
|
|
ReturnStatement: 24,
|
|
|
|
LabeledStatement: 25,
|
|
|
|
BreakStatement: 26,
|
|
|
|
ContinueStatement: 27,
|
|
|
|
IfStatement: 28,
|
|
|
|
SwitchStatement: 29,
|
2024-12-05 01:18:08 +01:00
|
|
|
SwitchCase: 30,
|
2024-12-06 23:26:11 +01:00
|
|
|
ThrowStatement: 31,
|
|
|
|
TryStatement: 32,
|
|
|
|
WhileStatement: 33,
|
|
|
|
DoWhileStatement: 34,
|
|
|
|
ForStatement: 35,
|
|
|
|
ForInStatement: 36,
|
|
|
|
ForOfStatement: 37,
|
2024-12-05 01:18:08 +01:00
|
|
|
Decl: 38,
|
2024-12-06 23:26:11 +01:00
|
|
|
ExpressionStatement: 39,
|
2024-12-05 01:18:08 +01:00
|
|
|
|
|
|
|
// Expressions
|
|
|
|
This: 40,
|
2024-12-06 23:26:11 +01:00
|
|
|
ArrayExpression: 41,
|
|
|
|
ObjectExpression: 42,
|
|
|
|
FunctionExpression: 43,
|
2024-12-05 01:18:08 +01:00
|
|
|
Unary: 44,
|
|
|
|
Update: 45,
|
2024-12-06 23:26:11 +01:00
|
|
|
BinaryExpression: 46,
|
|
|
|
AssignmentExpression: 47,
|
|
|
|
MemberExpression: 48,
|
2024-12-05 01:18:08 +01:00
|
|
|
SuperProp: 49,
|
2024-12-06 23:26:11 +01:00
|
|
|
ConditionalExpression: 50,
|
|
|
|
CallExpression: 51,
|
|
|
|
NewExpression: 52,
|
|
|
|
SequenceExpression: 53,
|
|
|
|
Identifier: 54,
|
2024-12-05 01:18:08 +01:00
|
|
|
Tpl: 55,
|
2024-12-06 23:26:11 +01:00
|
|
|
TaggedTemplateExpression: 56,
|
|
|
|
ArrowFunctionExpression: 57,
|
2024-12-05 01:18:08 +01:00
|
|
|
Yield: 59,
|
2024-12-06 23:26:11 +01:00
|
|
|
AwaitExpression: 61,
|
2024-12-05 01:18:08 +01:00
|
|
|
|
|
|
|
StringLiteral: 70,
|
2024-12-06 23:26:11 +01:00
|
|
|
BooleanLiteral: 71,
|
|
|
|
NullLiteral: 72,
|
|
|
|
NumericLiteral: 73,
|
2024-12-05 01:18:08 +01:00
|
|
|
BigInt: 74,
|
2024-12-06 23:26:11 +01:00
|
|
|
RegExpLiteral: 75,
|
2024-12-05 01:18:08 +01:00
|
|
|
|
|
|
|
// Custom
|
2024-12-05 02:24:26 +01:00
|
|
|
EmptyExpr: 82,
|
2024-12-05 01:18:08 +01:00
|
|
|
Spread: 83,
|
|
|
|
ObjProperty: 84,
|
|
|
|
VarDeclarator: 85,
|
2024-12-05 02:24:26 +01:00
|
|
|
CatchClause: 86,
|
2024-12-04 16:21:17 +01:00
|
|
|
};
|
|
|
|
|
2024-12-05 01:18:08 +01:00
|
|
|
const _ID = Symbol.for("__astId");
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.Program} */
|
2024-12-05 01:18:08 +01:00
|
|
|
class Program {
|
2024-12-06 23:26:11 +01:00
|
|
|
type = /** @type {const} */ ("Program");
|
|
|
|
range;
|
|
|
|
get body() {
|
|
|
|
return [];
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {Deno.Program["sourceType"]} sourceType
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, sourceType) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
|
|
|
this.sourceType = sourceType;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.BlockStatement} */
|
|
|
|
class BlockStatement {
|
|
|
|
type = /** @type {const} */ ("BlockStatement");
|
|
|
|
body = [];
|
|
|
|
range;
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
*/
|
|
|
|
constructor(ctx, range) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.BreakStatement} */
|
|
|
|
class BreakStatement {
|
|
|
|
type = /** @type {const} */ ("BreakStatement");
|
|
|
|
get label() {
|
|
|
|
if (this.#labelId === 0) return null;
|
|
|
|
return /** @type {Deno.Identifier} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#labelId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
range;
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
#labelId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} labelId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, labelId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#labelId = labelId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ContinueStatement} */
|
|
|
|
class ContinueStatement {
|
|
|
|
type = /** @type {const} */ ("ContinueStatement");
|
|
|
|
range;
|
|
|
|
get label() {
|
|
|
|
return /** @type {Deno.Identifier} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#labelId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#labelId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} labelId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, labelId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#labelId = labelId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.DebuggerStatement} */
|
|
|
|
class DebuggerStatement {
|
|
|
|
type = /** @type {const} */ ("DebuggerStatement");
|
|
|
|
range;
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
*/
|
|
|
|
constructor(ctx, range) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.DoWhileStatement} */
|
|
|
|
class DoWhileStatement {
|
|
|
|
type = /** @type {const} */ ("DoWhileStatement");
|
|
|
|
range;
|
|
|
|
get test() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#exprId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#exprId = 0;
|
|
|
|
#bodyId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} exprId
|
|
|
|
* @param {number} bodyId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, exprId, bodyId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#exprId = exprId;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ExpressionStatement} */
|
|
|
|
class ExpressionStatement {
|
|
|
|
type = /** @type {const} */ ("ExpressionStatement");
|
|
|
|
range;
|
|
|
|
get expression() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#exprId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#exprId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} exprId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, exprId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#exprId = exprId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ForInStatement} */
|
2024-12-05 01:18:08 +01:00
|
|
|
class ForInStatement {
|
2024-12-06 23:26:11 +01:00
|
|
|
type = /** @type {const} */ ("ForInStatement");
|
|
|
|
range;
|
|
|
|
get left() {
|
|
|
|
return /** @type {Deno.Expression | Deno.VariableDeclaration} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#leftId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get right() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#rightId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#leftId = 0;
|
|
|
|
#rightId = 0;
|
|
|
|
#bodyId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} leftId
|
|
|
|
* @param {number} rightId
|
|
|
|
* @param {number} bodyId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, leftId, rightId, bodyId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#leftId = leftId;
|
|
|
|
this.#rightId = rightId;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ForOfStatement} */
|
2024-12-05 01:18:08 +01:00
|
|
|
class ForOfStatement {
|
2024-12-06 23:26:11 +01:00
|
|
|
type = /** @type {const} */ ("ForOfStatement");
|
|
|
|
range;
|
|
|
|
get left() {
|
|
|
|
return /** @type {Deno.Expression | Deno.VariableDeclaration | Deno.UsingDeclaration} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#leftId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get right() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#rightId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
await;
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
#leftId = 0;
|
|
|
|
#rightId = 0;
|
|
|
|
#bodyId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {boolean} isAwait
|
|
|
|
* @param {number} leftId
|
|
|
|
* @param {number} rightId
|
|
|
|
* @param {number} bodyId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, isAwait, leftId, rightId, bodyId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#leftId = leftId;
|
|
|
|
this.#rightId = rightId;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.range = range;
|
|
|
|
this.await = isAwait;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ForStatement} */
|
|
|
|
class ForStatement {
|
|
|
|
type = /** @type {const} */ ("ForStatement");
|
|
|
|
range;
|
|
|
|
get init() {
|
|
|
|
if (this.#initId === 0) return null;
|
|
|
|
|
|
|
|
return /** @type {Deno.Expression | Deno.VariableDeclaration} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#initId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get test() {
|
|
|
|
if (this.#initId === 0) return null;
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#testId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get update() {
|
|
|
|
if (this.#updateId === 0) return null;
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#updateId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#initId = 0;
|
|
|
|
#testId = 0;
|
|
|
|
#updateId = 0;
|
|
|
|
#bodyId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} initId
|
|
|
|
* @param {number} testId
|
|
|
|
* @param {number} updateId
|
|
|
|
* @param {number} bodyId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, initId, testId, updateId, bodyId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#initId = initId;
|
|
|
|
this.#testId = testId;
|
|
|
|
this.#updateId = updateId;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.IfStatement} */
|
|
|
|
class IfStatement {
|
|
|
|
type = /** @type {const} */ ("IfStatement");
|
|
|
|
range;
|
|
|
|
get test() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#testId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get consequent() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#consequentId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get alternate() {
|
|
|
|
if (this.#alternateId === 0) return null;
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#alternateId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#testId = 0;
|
|
|
|
#consequentId = 0;
|
|
|
|
#alternateId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} testId
|
|
|
|
* @param {number} updateId
|
|
|
|
* @param {number} alternateId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, testId, updateId, alternateId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#testId = testId;
|
|
|
|
this.#consequentId = updateId;
|
|
|
|
this.#alternateId = alternateId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.LabeledStatement} */
|
|
|
|
class LabeledStatement {
|
|
|
|
type = /** @type {const} */ ("LabeledStatement");
|
|
|
|
range;
|
|
|
|
get label() {
|
|
|
|
return /** @type {Deno.Identifier} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#labelId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 02:24:26 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#labelId = 0;
|
|
|
|
#bodyId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} testId
|
|
|
|
* @param {number} bodyId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, testId, bodyId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#labelId = testId;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.range = range;
|
2024-12-05 02:24:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ReturnStatement} */
|
|
|
|
class ReturnStatement {
|
|
|
|
type = /** @type {const} */ ("ReturnStatement");
|
|
|
|
range;
|
|
|
|
get argument() {
|
|
|
|
if (this.#exprId === 0) return null;
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#exprId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 02:24:26 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#exprId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} argId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, argId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#exprId = argId;
|
|
|
|
this.range = range;
|
2024-12-05 02:24:26 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.SwitchStatement} */
|
|
|
|
class SwitchStatement {
|
|
|
|
type = /** @type {const} */ ("SwitchStatement");
|
|
|
|
range;
|
|
|
|
get discriminant() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#discriminantId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get cases() {
|
|
|
|
return []; // FIXME
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#discriminantId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} discriminantId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, discriminantId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#discriminantId = discriminantId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ThrowStatement} */
|
|
|
|
class ThrowStatement {
|
|
|
|
type = /** @type {const} */ ("ThrowStatement");
|
|
|
|
range;
|
|
|
|
get argument() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#argId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#argId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} argId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, argId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#argId = argId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.TryStatement} */
|
|
|
|
class TryStatement {
|
|
|
|
type = /** @type {const} */ ("TryStatement");
|
|
|
|
range;
|
|
|
|
get block() {
|
|
|
|
return /** @type {Deno.BlockStatement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#blockId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get finalizer() {
|
|
|
|
if (this.#finalizerId === 0) return null;
|
|
|
|
return /** @type {Deno.BlockStatement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#finalizerId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get handler() {
|
|
|
|
if (this.#handlerId === 0) return null;
|
|
|
|
return /** @type {Deno.CatchClause} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#handlerId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#blockId = 0;
|
|
|
|
#finalizerId = 0;
|
|
|
|
#handlerId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} blockId
|
|
|
|
* @param {number} finalizerId
|
|
|
|
* @param {number} handlerId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, blockId, finalizerId, handlerId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#blockId = blockId;
|
|
|
|
this.#finalizerId = finalizerId;
|
|
|
|
this.#handlerId = handlerId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.WhileStatement} */
|
|
|
|
class WhileStatement {
|
|
|
|
type = /** @type {const} */ ("WhileStatement");
|
|
|
|
range;
|
|
|
|
get test() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#testId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#testId = 0;
|
|
|
|
#bodyId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} testId
|
|
|
|
* @param {number} bodyId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, testId, bodyId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#testId = testId;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.WithStatement} */
|
|
|
|
class WithStatement {
|
|
|
|
type = /** @type {const} */ ("WithStatement");
|
|
|
|
range;
|
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.Statement} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get object() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#objectId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#bodyId = 0;
|
|
|
|
#objectId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} bodyId
|
|
|
|
* @param {number} objectId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, bodyId, objectId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.#objectId = objectId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
// Expressions
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ArrayExpression} */
|
|
|
|
class ArrayExpression {
|
|
|
|
type = /** @type {const} */ ("ArrayExpression");
|
|
|
|
range;
|
|
|
|
get elements() {
|
|
|
|
return []; // FIXME
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
*/
|
|
|
|
constructor(ctx, range) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ArrowFunctionExpression} */
|
|
|
|
class ArrowFunctionExpression {
|
|
|
|
type = /** @type {const} */ ("ArrowFunctionExpression");
|
|
|
|
range;
|
|
|
|
async = false;
|
|
|
|
generator = false;
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
get body() {
|
|
|
|
return /** @type {Deno.BlockStatement| Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#bodyId,
|
|
|
|
));
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
get params() {
|
|
|
|
return []; // FIXME
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
get returnType() {
|
|
|
|
return null; // FIXME
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
get typeParameters() {
|
|
|
|
return null; // FIXME
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#bodyId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {boolean} isAsync
|
|
|
|
* @param {boolean} isGenerator
|
|
|
|
* @param {number} bodyId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, isAsync, isGenerator, bodyId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#bodyId = bodyId;
|
|
|
|
this.asnyc = isAsync;
|
|
|
|
this.generator = isGenerator;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.AssignmentExpression} */
|
|
|
|
class AssignmentExpression {
|
|
|
|
type = /** @type {const} */ ("AssignmentExpression");
|
|
|
|
range;
|
|
|
|
get left() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#leftId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get right() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#rightId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
operator;
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
#leftId = 0;
|
|
|
|
#rightId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
* @param {number} leftId
|
|
|
|
* @param {number} rightId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags, leftId, rightId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#leftId = leftId;
|
|
|
|
this.#rightId = rightId;
|
|
|
|
this.range = range;
|
|
|
|
this.operator = getAssignOperator(flags);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {number} n
|
|
|
|
* @returns {Deno.AssignmentExpression["operator"]}
|
|
|
|
*/
|
|
|
|
function getAssignOperator(n) {
|
|
|
|
switch (n) {
|
|
|
|
case 0:
|
|
|
|
return "&&=";
|
|
|
|
case 1:
|
|
|
|
return "&=";
|
|
|
|
case 2:
|
|
|
|
return "**=";
|
|
|
|
case 3:
|
|
|
|
return "*=";
|
|
|
|
case 4:
|
|
|
|
return "||=";
|
|
|
|
case 5:
|
|
|
|
return "|=";
|
|
|
|
case 6:
|
|
|
|
return "^=";
|
|
|
|
case 7:
|
|
|
|
return "=";
|
|
|
|
case 8:
|
|
|
|
return ">>=";
|
|
|
|
case 9:
|
|
|
|
return ">>>=";
|
|
|
|
case 10:
|
|
|
|
return "<<=";
|
|
|
|
case 11:
|
|
|
|
return "-=";
|
|
|
|
case 12:
|
|
|
|
return "%=";
|
|
|
|
case 13:
|
|
|
|
return "+=";
|
|
|
|
case 14:
|
|
|
|
return "??=";
|
|
|
|
case 15:
|
|
|
|
return "/=";
|
|
|
|
default:
|
|
|
|
throw new Error(`Unknown operator: ${n}`);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.AwaitExpression} */
|
|
|
|
class AwaitExpression {
|
|
|
|
type = /** @type {const} */ ("AwaitExpression");
|
|
|
|
range;
|
|
|
|
get argument() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#argId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#argId = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} argId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, argId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#argId = argId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.BinaryExpression} */
|
|
|
|
class BinaryExpression {
|
|
|
|
type = /** @type {const} */ ("BinaryExpression");
|
|
|
|
range;
|
|
|
|
get left() {
|
|
|
|
return /** @type {Deno.Expression | Deno.PrivateIdentifier} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#leftId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get right() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#rightId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
operator;
|
|
|
|
#ctx;
|
|
|
|
#leftId;
|
|
|
|
#rightId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
* @param {number} leftId
|
|
|
|
* @param {number} rightId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags, leftId, rightId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#leftId = leftId;
|
|
|
|
this.#rightId = rightId;
|
|
|
|
this.operator = getBinaryOperator(flags);
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {number} n
|
|
|
|
* @returns {Deno.BinaryExpression["operator"]}
|
|
|
|
*/
|
|
|
|
function getBinaryOperator(n) {
|
|
|
|
switch (n) {
|
|
|
|
case 0:
|
|
|
|
return "&";
|
|
|
|
case 1:
|
|
|
|
return "**";
|
|
|
|
case 2:
|
|
|
|
return "*";
|
|
|
|
case 3:
|
|
|
|
return "|";
|
|
|
|
case 4:
|
|
|
|
return "^";
|
|
|
|
case 5:
|
|
|
|
return "===";
|
|
|
|
case 6:
|
|
|
|
return "==";
|
|
|
|
case 7:
|
|
|
|
return "!==";
|
|
|
|
case 8:
|
|
|
|
return "!=";
|
|
|
|
case 9:
|
|
|
|
return ">=";
|
|
|
|
case 10:
|
|
|
|
return ">>>";
|
|
|
|
case 11:
|
|
|
|
return ">>";
|
|
|
|
case 12:
|
|
|
|
return ">";
|
|
|
|
case 13:
|
|
|
|
return "in";
|
|
|
|
case 14:
|
|
|
|
return "instanceof";
|
|
|
|
case 15:
|
|
|
|
return "<=";
|
|
|
|
case 16:
|
|
|
|
return "<<";
|
|
|
|
case 17:
|
|
|
|
return "<";
|
|
|
|
case 18:
|
|
|
|
return "-";
|
|
|
|
case 19:
|
|
|
|
return "%";
|
|
|
|
case 20:
|
|
|
|
return "+";
|
|
|
|
case 21:
|
|
|
|
return "/";
|
|
|
|
default:
|
|
|
|
throw new Error(`Unknown operator: ${n}`);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.CallExpression} */
|
|
|
|
class CallExpression {
|
|
|
|
type = /** @type {const} */ ("CallExpression");
|
|
|
|
range;
|
|
|
|
get callee() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#calleeId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get arguments() {
|
|
|
|
return []; // FIXME
|
|
|
|
}
|
|
|
|
get typeArguments() {
|
|
|
|
return null; // FIXME
|
|
|
|
}
|
|
|
|
|
|
|
|
optional = false; // FIXME
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
#calleeId;
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} calleeId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, calleeId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#calleeId = calleeId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ChainExpression} */
|
|
|
|
class ChainExpression {
|
|
|
|
type = /** @type {const} */ ("ChainExpression");
|
|
|
|
range;
|
|
|
|
|
|
|
|
#ctx;
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
*/
|
|
|
|
constructor(ctx, range) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.ConditionalExpression} */
|
|
|
|
class ConditionalExpression {
|
|
|
|
type = /** @type {const} */ ("ConditionalExpression");
|
|
|
|
range;
|
|
|
|
get test() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#testId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get consequent() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#consequentId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get alternate() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#alternateId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#testId;
|
|
|
|
#consequentId;
|
|
|
|
#alternateId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} testId
|
|
|
|
* @param {number} consequentId
|
|
|
|
* @param {number} alternateId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, testId, consequentId, alternateId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.#testId = testId;
|
|
|
|
this.#consequentId = consequentId;
|
|
|
|
this.#alternateId = alternateId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.FunctionExpression} */
|
|
|
|
class FunctionExpression {
|
|
|
|
type = /** @type {const} */ ("FunctionExpression");
|
|
|
|
range;
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
*/
|
|
|
|
constructor(ctx, range) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.Identifier} */
|
|
|
|
class Identifier {
|
|
|
|
type = /** @type {const} */ ("Identifier");
|
|
|
|
range;
|
|
|
|
name = "";
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.name = getString(ctx, flags);
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.LogicalExpression} */
|
|
|
|
class LogicalExpression {
|
|
|
|
type = /** @type {const} */ ("LogicalExpression");
|
|
|
|
range;
|
|
|
|
get left() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#leftId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get right() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#rightId,
|
|
|
|
));
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
#leftId;
|
|
|
|
#rightId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
* @param {number} leftId
|
|
|
|
* @param {number} rightId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags, leftId, rightId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.operator = getLogicalOperator(flags);
|
|
|
|
this.#leftId = leftId;
|
|
|
|
this.#rightId = rightId;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.MemberExpression} */
|
|
|
|
class MemberExpression {
|
|
|
|
type = /** @type {const} */ ("MemberExpression");
|
|
|
|
range;
|
|
|
|
get object() {
|
|
|
|
return /** @type {Deno.Expression} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#objId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
get property() {
|
|
|
|
return /** @type {Deno.Expression | Deno.Identifier | Deno.PrivateIdentifier} */ (createAstNode(
|
|
|
|
this.#ctx,
|
|
|
|
this.#propId,
|
|
|
|
));
|
|
|
|
}
|
|
|
|
optional = false; // FIXME
|
|
|
|
computed = false; // FIXME
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
#objId;
|
|
|
|
#propId;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
* @param {number} objId
|
|
|
|
* @param {number} propId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags, objId, propId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.computed = flags === 1;
|
|
|
|
this.#objId = objId;
|
|
|
|
this.#propId = propId;
|
|
|
|
this.range = range;
|
|
|
|
}
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {number} n
|
|
|
|
* @returns {Deno.LogicalExpression["operator"]}
|
|
|
|
*/
|
|
|
|
function getLogicalOperator(n) {
|
|
|
|
switch (n) {
|
|
|
|
case 0:
|
|
|
|
return "&&";
|
|
|
|
case 1:
|
|
|
|
return "||";
|
|
|
|
case 2:
|
|
|
|
return "??";
|
|
|
|
default:
|
|
|
|
throw new Error(`Unknown operator: ${n}`);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
// Literals
|
|
|
|
|
|
|
|
/** @implements {Deno.BooleanLiteral} */
|
2024-12-05 01:18:08 +01:00
|
|
|
class BooleanLiteral {
|
2024-12-06 23:26:11 +01:00
|
|
|
type = /** @type {const} */ ("BooleanLiteral");
|
|
|
|
range;
|
2024-12-05 01:18:08 +01:00
|
|
|
value = false;
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.value = flags === 1;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.NullLiteral} */
|
2024-12-05 01:18:08 +01:00
|
|
|
class NullLiteral {
|
2024-12-06 23:26:11 +01:00
|
|
|
type = /** @type {const} */ ("NullLiteral");
|
|
|
|
range;
|
|
|
|
value = null;
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
*/
|
|
|
|
constructor(ctx, range) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.NumericLiteral} */
|
2024-12-05 01:18:08 +01:00
|
|
|
class NumericLiteral {
|
2024-12-06 23:26:11 +01:00
|
|
|
type = /** @type {const} */ ("NumericLiteral");
|
|
|
|
range;
|
2024-12-05 01:18:08 +01:00
|
|
|
value = 0;
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
|
|
|
this.value = flags;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.RegExpLiteral} */
|
2024-12-05 01:18:08 +01:00
|
|
|
class RegExpLiteral {
|
2024-12-06 23:26:11 +01:00
|
|
|
type = /** @type {const} */ ("RegExpLiteral");
|
|
|
|
range;
|
2024-12-05 01:18:08 +01:00
|
|
|
pattern = "";
|
|
|
|
flags = "";
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} patternId
|
|
|
|
* @param {number} flagsId
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, patternId, flagsId) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
|
|
|
this.pattern = getString(ctx, patternId);
|
|
|
|
this.flags = getString(ctx, flagsId);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/** @implements {Deno.StringLiteral} */
|
|
|
|
class StringLiteral {
|
|
|
|
type = /** @type {const} */ ("StringLiteral");
|
|
|
|
range;
|
|
|
|
value = "";
|
|
|
|
|
|
|
|
#ctx;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {Deno.Range} range
|
|
|
|
* @param {number} flags
|
|
|
|
*/
|
|
|
|
constructor(ctx, range, flags) {
|
|
|
|
this.#ctx = ctx;
|
|
|
|
this.range = range;
|
|
|
|
this.value = getString(ctx, flags);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
const DECODER = new TextDecoder();
|
2024-12-05 02:24:26 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @typedef {{
|
|
|
|
* buf: Uint8Array,
|
|
|
|
* strTable: Map<number, string>,
|
|
|
|
* idTable: number[]
|
|
|
|
* }} ParseContext
|
|
|
|
*/
|
2024-12-05 02:24:26 +01:00
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {Uint8Array} buf
|
|
|
|
* @param {number} i
|
|
|
|
* @returns {number}
|
|
|
|
*/
|
|
|
|
function readU32(buf, i) {
|
|
|
|
return (buf[i] << 24) + (buf[i + 1] << 16) + (buf[i + 2] << 8) +
|
|
|
|
buf[i + 3];
|
2024-12-05 02:24:26 +01:00
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {number} id
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
function getString(ctx, id) {
|
|
|
|
const name = ctx.strTable.get(id);
|
|
|
|
if (name === undefined) {
|
|
|
|
throw new Error(`Missing string id: ${id}`);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
return name;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
|
2024-12-06 23:26:11 +01:00
|
|
|
/**
|
|
|
|
* @param {ParseContext} ctx
|
|
|
|
* @param {number} id
|
|
|
|
* @returns {Deno.AstNode}
|
|
|
|
*/
|
|
|
|
function createAstNode(ctx, id) {
|
|
|
|
const { buf, idTable } = ctx;
|
|
|
|
const i = idTable[id];
|
|
|
|
/** @type {AstNodeId} */
|
|
|
|
const kind = buf[i];
|
|
|
|
|
|
|
|
const flags = buf[i];
|
|
|
|
const count = readU32(buf, i + 2);
|
|
|
|
const rangeStart = readU32(buf, i + 6);
|
|
|
|
const rangeEnd = readU32(buf, i + 10);
|
|
|
|
const range = /** @type {Deno.Range} */ ([rangeStart, rangeEnd]);
|
|
|
|
|
|
|
|
switch (kind) {
|
|
|
|
case AstNodeId.Program:
|
|
|
|
return new Program(ctx, range, flags === 1 ? "module" : "script");
|
|
|
|
case AstNodeId.Import:
|
|
|
|
// case AstNodeId.ImportDecl:
|
|
|
|
// case AstNodeId.ExportDecl:
|
|
|
|
// case AstNodeId.ExportNamed:
|
|
|
|
// case AstNodeId.ExportDefaultDecl:
|
|
|
|
// case AstNodeId.ExportDefaultExpr:
|
|
|
|
// case AstNodeId.ExportAll:
|
|
|
|
|
|
|
|
// Statements
|
|
|
|
case AstNodeId.BlockStatement:
|
|
|
|
throw new BlockStatement(ctx, range); // FIXME
|
|
|
|
case AstNodeId.BreakStatement:
|
|
|
|
throw new BreakStatement(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.ContinueStatement:
|
|
|
|
throw new ContinueStatement(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.DebuggerStatement:
|
|
|
|
throw new DebuggerStatement(ctx, range);
|
|
|
|
case AstNodeId.DoWhileStatement:
|
|
|
|
throw new DoWhileStatement(ctx, range, 0, 0); // FIXME
|
|
|
|
case AstNodeId.ExpressionStatement:
|
|
|
|
return new ExpressionStatement(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.ForInStatement:
|
|
|
|
throw new ForInStatement(ctx, range, 0, 0, 0); // FIXME
|
|
|
|
case AstNodeId.ForOfStatement:
|
|
|
|
throw new ForOfStatement(ctx, range, false, 0, 0, 0); // FIXME
|
|
|
|
case AstNodeId.ForStatement:
|
|
|
|
throw new ForStatement(ctx, range, 0, 0, 0, 0); // FIXME
|
|
|
|
case AstNodeId.IfStatement:
|
|
|
|
throw new IfStatement(ctx, range, 0, 0, 0); // FIXME
|
|
|
|
case AstNodeId.LabeledStatement:
|
|
|
|
throw new LabeledStatement(ctx, range, 0, 0); // FIXME
|
|
|
|
case AstNodeId.ReturnStatement:
|
|
|
|
throw new ReturnStatement(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.SwitchStatement:
|
|
|
|
throw new SwitchStatement(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.ThrowStatement:
|
|
|
|
throw new ThrowStatement(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.TryStatement:
|
|
|
|
throw new TryStatement(ctx, range, 0, 0, 0); // FIXME
|
|
|
|
case AstNodeId.WhileStatement:
|
|
|
|
throw new WhileStatement(ctx, range, 0, 0); // FIXME
|
|
|
|
case AstNodeId.WithStatement:
|
|
|
|
throw new WithStatement(ctx, range, 0, 0);
|
|
|
|
|
|
|
|
// Expressions
|
|
|
|
case AstNodeId.ArrayExpression:
|
|
|
|
throw new ArrayExpression(ctx, range); // FIXME
|
|
|
|
case AstNodeId.ArrowFunctionExpression:
|
|
|
|
throw new ArrowFunctionExpression(ctx, range, false, false, 0); // FIXME
|
|
|
|
case AstNodeId.AssignmentExpression:
|
|
|
|
throw new AssignmentExpression(ctx, range, flags, 0, 0); // FIXME
|
|
|
|
case AstNodeId.AwaitExpression:
|
|
|
|
throw new AwaitExpression(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.BinaryExpression:
|
|
|
|
throw new BinaryExpression(ctx, range, flags, 0, 0); // FIXME
|
|
|
|
case AstNodeId.CallExpression:
|
|
|
|
throw new CallExpression(ctx, range, 0); // FIXME
|
|
|
|
case AstNodeId.ChainExpression:
|
|
|
|
throw new ChainExpression(ctx, range); // FIXME
|
|
|
|
case AstNodeId.ConditionalExpression:
|
|
|
|
throw new ConditionalExpression(ctx, range, 0, 0, 0); // FIXME
|
|
|
|
case AstNodeId.FunctionExpression:
|
|
|
|
throw new FunctionExpression(ctx, range); // FIXME
|
|
|
|
case AstNodeId.Identifier:
|
|
|
|
throw new Identifier(ctx, range, flags); // FIXME
|
|
|
|
case AstNodeId.LogicalExpression:
|
|
|
|
throw new LogicalExpression(ctx, range, flags, 0, 0); // FIXME
|
|
|
|
case AstNodeId.MemberExpression:
|
|
|
|
throw new MemberExpression(ctx, range, flags, 0, 0); // FIXME
|
|
|
|
case AstNodeId.MetaProperty:
|
|
|
|
throw new Error("TODO");
|
|
|
|
case AstNodeId.NewExpression:
|
|
|
|
throw new Error("TODO");
|
|
|
|
case AstNodeId.ObjectExpression:
|
|
|
|
throw new Error("TODO");
|
|
|
|
case AstNodeId.StaticBlock:
|
|
|
|
throw new Error("TODO");
|
|
|
|
case AstNodeId.SequenceExpression:
|
|
|
|
throw new Error("TODO");
|
|
|
|
case AstNodeId.Super:
|
|
|
|
throw new Error("TODO");
|
|
|
|
case AstNodeId.TaggedTemplateExpression:
|
|
|
|
throw new Error("TODO");
|
|
|
|
case AstNodeId.TemplateLiteral:
|
|
|
|
throw new Error("TODO");
|
|
|
|
|
|
|
|
// Literals
|
|
|
|
case AstNodeId.BooleanLiteral:
|
|
|
|
throw new BooleanLiteral(ctx, range, flags); // FIXME
|
|
|
|
case AstNodeId.NullLiteral:
|
|
|
|
throw new NullLiteral(ctx, range); // FIXME
|
|
|
|
case AstNodeId.NumericLiteral:
|
|
|
|
throw new NumericLiteral(ctx, range, flags); // FIXME
|
|
|
|
case AstNodeId.RegExpLiteral:
|
|
|
|
throw new RegExpLiteral(ctx, range, flags); // FIXME
|
|
|
|
case AstNodeId.StringLiteral:
|
|
|
|
throw new StringLiteral(ctx, range, flags); // FIXME
|
|
|
|
default:
|
|
|
|
throw new Error(`Unknown ast node ${kind}`);
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-04 16:21:17 +01:00
|
|
|
/**
|
|
|
|
* @param {Uint8Array} ast
|
|
|
|
*/
|
|
|
|
function buildAstFromBinary(ast) {
|
2024-12-05 11:21:34 +01:00
|
|
|
console.log(ast);
|
|
|
|
|
|
|
|
// Extract string table
|
|
|
|
/** @type {Map<number, string>} */
|
|
|
|
const strTable = new Map();
|
|
|
|
|
|
|
|
let start = 0;
|
|
|
|
const stringCount = (ast[0] << 24) + (ast[1] << 16) + (ast[2] << 8) +
|
|
|
|
ast[3];
|
|
|
|
start += 4;
|
|
|
|
|
|
|
|
let id = 0;
|
|
|
|
while (id < stringCount) {
|
|
|
|
const len = (ast[start] << 24) + (ast[start + 1] << 16) +
|
|
|
|
(ast[start + 2] << 8) +
|
|
|
|
ast[start + 3];
|
|
|
|
start += 4;
|
|
|
|
|
|
|
|
const strBytes = ast.slice(start, start + len);
|
|
|
|
console.log({ strBytes });
|
|
|
|
start += len;
|
|
|
|
const s = DECODER.decode(strBytes);
|
|
|
|
strTable.set(id, s);
|
|
|
|
id++;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log({ stringCount, strTable });
|
|
|
|
|
|
|
|
if (strTable.size !== stringCount) {
|
|
|
|
throw new Error(
|
|
|
|
`Could not deserialize string table. Expected ${stringCount} items, but got ${strTable.size}`,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-12-05 01:18:08 +01:00
|
|
|
const counts = [];
|
2024-12-04 16:21:17 +01:00
|
|
|
const stack = [];
|
2024-12-05 11:21:34 +01:00
|
|
|
for (let i = start; i < ast.length; i += 14) {
|
2024-12-04 16:21:17 +01:00
|
|
|
const kind = ast[i];
|
2024-12-05 01:18:08 +01:00
|
|
|
const flags = ast[i + 1];
|
2024-12-05 11:21:34 +01:00
|
|
|
let count = (ast[i + 2] << 24) + (ast[i + 3] << 16) + (ast[i + 4] << 8) +
|
|
|
|
ast[i + 5];
|
|
|
|
const start = (ast[i + 6] << 24) + (ast[i + 7] << 16) + (ast[i + 8] << 8) +
|
|
|
|
ast[i + 9];
|
|
|
|
const end = (ast[i + 10] << 24) + (ast[i + 11] << 16) +
|
|
|
|
(ast[i + 12] << 8) +
|
|
|
|
ast[i + 13];
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-05 14:34:22 +01:00
|
|
|
const span = [start, end];
|
2024-12-05 01:18:08 +01:00
|
|
|
|
|
|
|
let node = null;
|
|
|
|
switch (kind) {
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Program:
|
2024-12-05 11:21:34 +01:00
|
|
|
node = new Program(span, flags === 1 ? "module" : "script");
|
2024-12-05 01:18:08 +01:00
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Var:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new VariableDeclaration(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.VarDeclarator:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new VariableDeclarator(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ExpressionStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ExpressionStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.This:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ThisExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ArrayExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ArrayExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ObjectExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ObjectExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.AssignmentExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ObjectExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.MemberExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new MemberExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.CallExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new CallExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.SequenceExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new SequenceExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ObjProperty:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ObjectProperty(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ArrowFunctionExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ArrowFunctionExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.BlockStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new BlockStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.StringLiteral:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new StringLiteral(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Identifier:
|
2024-12-05 11:21:34 +01:00
|
|
|
node = new Identifier(span, strTable.get(count));
|
|
|
|
count = 0;
|
2024-12-05 01:18:08 +01:00
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Fn:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new FunctionDeclaration(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ReturnStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ReturnStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.IfStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new IfStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.BinaryExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new LogicalExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Unary:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new UnaryExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Update:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new UpdateExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ForStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ForStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.BooleanLiteral:
|
2024-12-05 11:21:34 +01:00
|
|
|
node = new BooleanLiteral(span, flags === 1);
|
2024-12-05 01:18:08 +01:00
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.NullLiteral:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new NullLiteral(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.NumericLiteral:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new NumericLiteral(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.RegExpLiteral:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new RegExpLiteral(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ForInStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ForInStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ForOfStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ForOfStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.WhileStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new WhileStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Yield:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new YieldExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ContinueStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ContinueStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.BreakStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new BreakStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ConditionalExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ConditionalExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.SwitchStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new SwitchStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.SwitchCase:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new SwitchCase(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.LabeledStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new LabeledStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.DoWhileStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new DoWhileStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Spread:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new SpreadElement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ThrowStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ThrowStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.DebuggerStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new DebuggerStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Tpl:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new TemplateLiteral(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.NewExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new NewExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Class:
|
2024-12-05 01:18:08 +01:00
|
|
|
node = new ClassDeclaration(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.TryStatement:
|
2024-12-05 02:24:26 +01:00
|
|
|
node = new TryStatement(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.CatchClause:
|
2024-12-05 02:24:26 +01:00
|
|
|
node = new CatchClause(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.TaggedTemplateExpression:
|
2024-12-05 02:24:26 +01:00
|
|
|
node = new TaggedTemplateExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.FunctionExpression:
|
2024-12-05 02:24:26 +01:00
|
|
|
node = new FunctionExpression(span);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Empty:
|
2024-12-05 02:24:26 +01:00
|
|
|
// Ignore empty statements
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.EmptyExpr:
|
2024-12-05 02:24:26 +01:00
|
|
|
// Nothing, AST defaults to null
|
|
|
|
break;
|
2024-12-05 01:18:08 +01:00
|
|
|
default:
|
|
|
|
throw new Error(`Unknown node: ${kind}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
// append node
|
|
|
|
if (stack.length > 0) {
|
|
|
|
const last = stack[stack.length - 1];
|
|
|
|
const id = last[_ID];
|
|
|
|
const lastCount = counts[counts.length - 1];
|
|
|
|
|
2024-12-05 02:24:26 +01:00
|
|
|
// console.log({ last, node });
|
2024-12-05 01:18:08 +01:00
|
|
|
switch (id) {
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Program:
|
|
|
|
case AstNodeId.BlockStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
last.body.push(node);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ExpressionStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
last.expression = node;
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ObjProperty:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount > 1) {
|
|
|
|
last.value = node;
|
|
|
|
} else {
|
|
|
|
last.key = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.MemberExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount > 1) {
|
|
|
|
last.property = node;
|
|
|
|
} else {
|
|
|
|
last.object = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.CallExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount > 1) {
|
|
|
|
last.arguments.push(node);
|
|
|
|
} else {
|
|
|
|
last.callee = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.SequenceExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
last.expressions.push(node);
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ArrowFunctionExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
// FIXME
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ReturnStatement:
|
|
|
|
case AstNodeId.Spread:
|
|
|
|
case AstNodeId.ThrowStatement:
|
|
|
|
case AstNodeId.Unary:
|
|
|
|
case AstNodeId.Update:
|
|
|
|
case AstNodeId.Yield:
|
2024-12-05 01:18:08 +01:00
|
|
|
last.argument = node;
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.IfStatement:
|
|
|
|
case AstNodeId.ConditionalExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount === 3) {
|
|
|
|
last.alternate = node;
|
|
|
|
} else if (lastCount === 2) {
|
|
|
|
last.consequent = node;
|
|
|
|
} else {
|
|
|
|
last.test = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.BinaryExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount === 2) {
|
|
|
|
last.right = node;
|
|
|
|
} else {
|
|
|
|
last.left = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ForStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount === 4) {
|
|
|
|
last.body = node;
|
|
|
|
} else if (lastCount === 3) {
|
|
|
|
last.update = node;
|
|
|
|
} else if (lastCount === 2) {
|
|
|
|
last.test = node;
|
|
|
|
} else if (lastCount === 1) {
|
|
|
|
last.init = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.ForInStatement:
|
|
|
|
case AstNodeId.ForOfStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount === 3) {
|
|
|
|
last.body = node;
|
|
|
|
} else if (lastCount === 2) {
|
|
|
|
last.right = node;
|
|
|
|
} else {
|
|
|
|
last.left = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.DoWhileStatement:
|
|
|
|
case AstNodeId.WhileStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount === 2) {
|
|
|
|
last.body = node;
|
|
|
|
} else {
|
|
|
|
last.test = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.BreakStatement:
|
|
|
|
case AstNodeId.ContinueStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
last.label = node;
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.SwitchStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount > 1) {
|
|
|
|
last.cases.push(node);
|
|
|
|
} else {
|
|
|
|
last.discriminant = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.SwitchCase:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount > 1) {
|
|
|
|
last.consequent = node;
|
|
|
|
} else {
|
|
|
|
last.test = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.LabeledStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
last.body = node;
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.VarDeclarator:
|
2024-12-05 01:18:08 +01:00
|
|
|
if (lastCount > 1) {
|
|
|
|
last.init = node;
|
|
|
|
} else {
|
|
|
|
last.id = node;
|
|
|
|
}
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.NewExpression:
|
2024-12-05 01:18:08 +01:00
|
|
|
// FIXME
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Class:
|
2024-12-05 01:18:08 +01:00
|
|
|
// FIXME
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.TryStatement:
|
2024-12-05 02:24:26 +01:00
|
|
|
// FIXME
|
|
|
|
break;
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.CatchClause:
|
2024-12-05 02:24:26 +01:00
|
|
|
// FIXME
|
|
|
|
break;
|
2024-12-05 01:18:08 +01:00
|
|
|
// Can't happen
|
2024-12-06 23:26:11 +01:00
|
|
|
case AstNodeId.Identifier:
|
|
|
|
case AstNodeId.StringLiteral:
|
|
|
|
case AstNodeId.BigInt:
|
|
|
|
case AstNodeId.BooleanLiteral:
|
|
|
|
case AstNodeId.NullLiteral:
|
|
|
|
case AstNodeId.NumericLiteral:
|
|
|
|
case AstNodeId.RegExpLiteral:
|
|
|
|
case AstNodeId.This:
|
|
|
|
case AstNodeId.DebuggerStatement:
|
2024-12-05 01:18:08 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-12-05 02:24:26 +01:00
|
|
|
// console.log("APPENDED");
|
|
|
|
// console.log(last);
|
|
|
|
// console.log("======");
|
|
|
|
|
2024-12-05 01:18:08 +01:00
|
|
|
// Decrease count
|
2024-12-05 02:24:26 +01:00
|
|
|
const newCount = lastCount - 1;
|
|
|
|
counts[counts.length - 1] = newCount;
|
2024-12-05 01:18:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (count > 0) {
|
|
|
|
stack.push(node);
|
|
|
|
counts.push(count);
|
2024-12-05 02:24:26 +01:00
|
|
|
} else if (stack.length > 0) {
|
|
|
|
let lastCount = counts[counts.length - 1];
|
|
|
|
while (stack.length > 1 && lastCount === 0) {
|
|
|
|
// console.log({ counts, s: stack.map((x) => x.type) });
|
|
|
|
const l = stack.pop();
|
|
|
|
// console.log("POP", l);
|
|
|
|
lastCount = counts.pop();
|
|
|
|
}
|
2024-12-04 16:21:17 +01:00
|
|
|
}
|
|
|
|
}
|
2024-12-05 01:18:08 +01:00
|
|
|
|
2024-12-05 02:24:26 +01:00
|
|
|
// console.log(JSON.stringify(stack, null, 2));
|
2024-12-05 01:18:08 +01:00
|
|
|
return stack[0];
|
2024-12-04 16:21:17 +01:00
|
|
|
}
|
|
|
|
|
2024-12-05 11:21:34 +01:00
|
|
|
export function runPluginsForFile(fileName, serializedAst) {
|
|
|
|
const ast = buildAstFromBinary(serializedAst);
|
2024-12-05 14:34:22 +01:00
|
|
|
console.log(JSON.stringify(ast, null, 2));
|
2024-12-04 03:29:05 +01:00
|
|
|
|
2024-12-04 12:42:55 +01:00
|
|
|
/** @type {Record<string, (node: any) => void} */
|
|
|
|
const mergedVisitor = {};
|
|
|
|
const destroyFns = [];
|
|
|
|
|
|
|
|
// Instantiate and merge visitors. This allows us to only traverse
|
|
|
|
// the AST once instead of per plugin.
|
|
|
|
for (let i = 0; i < state.plugins; i++) {
|
|
|
|
const plugin = state.plugins[i];
|
|
|
|
|
|
|
|
for (const name of Object.keys(plugin)) {
|
|
|
|
const rule = plugin.rules[name];
|
|
|
|
const id = `${plugin.name}/${ruleName}`;
|
|
|
|
const ctx = new Context(id, fileName);
|
|
|
|
const visitor = rule.create(ctx);
|
|
|
|
|
|
|
|
for (const name in visitor) {
|
|
|
|
const prev = mergedVisitor[name];
|
|
|
|
mergedVisitor[name] = (node) => {
|
|
|
|
if (typeof prev === "function") {
|
|
|
|
prev(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
visitor[name](node);
|
|
|
|
} catch (err) {
|
|
|
|
throw new Error(`Visitor "${name}" of plugin "${id}" errored`, {
|
|
|
|
cause: err,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
mergedVisitor.push({ ctx, visitor, rule });
|
|
|
|
|
|
|
|
if (typeof rule.destroy === "function") {
|
|
|
|
destroyFns.push(() => {
|
|
|
|
try {
|
|
|
|
rule.destroy(ctx);
|
|
|
|
} catch (err) {
|
|
|
|
throw new Error(`Destroy hook of "${id}" errored`, { cause: err });
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-12-04 03:29:05 +01:00
|
|
|
}
|
2024-12-04 12:42:55 +01:00
|
|
|
}
|
2024-12-04 03:29:05 +01:00
|
|
|
|
2024-12-04 12:42:55 +01:00
|
|
|
// Traverse ast with all visitors at the same time to avoid traversing
|
|
|
|
// multiple times.
|
|
|
|
traverse(ast, mergedVisitor, null);
|
2024-12-04 03:29:05 +01:00
|
|
|
|
2024-12-04 12:42:55 +01:00
|
|
|
// Optional: Destroy rules
|
|
|
|
for (let i = 0; i < destroyFns.length; i++) {
|
|
|
|
destroyFns[i]();
|
2024-12-04 03:29:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-04 12:42:55 +01:00
|
|
|
/**
|
|
|
|
* @param {Record<string, any>} ast
|
|
|
|
* @param {*} visitor
|
|
|
|
* @param {any | null} parent
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
function traverse(ast, visitor, parent) {
|
2024-12-03 02:51:29 +01:00
|
|
|
if (!ast || typeof ast !== "object") {
|
|
|
|
return;
|
2024-12-01 05:14:46 +01:00
|
|
|
}
|
2024-12-03 02:51:29 +01:00
|
|
|
|
|
|
|
// Get node type, accounting for SWC's type property naming
|
|
|
|
const nodeType = ast.type || (ast.nodeType ? ast.nodeType : null);
|
|
|
|
|
|
|
|
// Skip if not a valid AST node
|
|
|
|
if (!nodeType) {
|
|
|
|
return;
|
2024-12-01 05:14:46 +01:00
|
|
|
}
|
2024-12-03 02:51:29 +01:00
|
|
|
|
|
|
|
ast.parent = parent;
|
2024-12-04 12:42:55 +01:00
|
|
|
|
2024-12-03 02:51:29 +01:00
|
|
|
// Call visitor if it exists for this node type
|
2024-12-04 12:42:55 +01:00
|
|
|
visitor[nodeType]?.(ast);
|
2024-12-03 02:51:29 +01:00
|
|
|
|
|
|
|
// Traverse child nodes
|
|
|
|
for (const key in ast) {
|
2024-12-03 03:11:26 +01:00
|
|
|
if (key === "parent" || key === "type") {
|
2024-12-03 02:51:29 +01:00
|
|
|
continue;
|
2024-12-01 05:14:46 +01:00
|
|
|
}
|
2024-12-03 02:51:29 +01:00
|
|
|
|
|
|
|
const child = ast[key];
|
|
|
|
|
|
|
|
if (Array.isArray(child)) {
|
2024-12-04 12:42:55 +01:00
|
|
|
for (let i = 0; i < child.length; i++) {
|
|
|
|
const item = child[i];
|
|
|
|
traverse(item, visitor, ast);
|
|
|
|
}
|
|
|
|
} else if (child !== null && typeof child === "object") {
|
2024-12-03 02:51:29 +01:00
|
|
|
traverse(child, visitor, ast);
|
2024-12-01 05:14:46 +01:00
|
|
|
}
|
|
|
|
}
|
2024-12-01 04:53:47 +01:00
|
|
|
}
|