AST 类型

本单元提供高效、模块化、, 埃斯皮里亚- 执行不兼容 页:1 抽象语法 树 类型等级 创始人: 莫齐拉·帕塞尔 API 密码。 。 。 .
安装
npm install ast-types
货包本身的TypeScript申报;没有
@types/ast-types 要安装。 TypeScript 4.4 及以后的支持,以及
对申报的每份申报都按4.4、4.9和5.x分类.
继续工作 ast-types 本身:
git clone https://github.com/benjamn/ast-types.git
cd ast-types
npm install
npm test
基本用途
import assert from "assert";
import {
namedTypes as n,
builders as b,
} from "ast-types";
var fooId = b.identifier("foo");
var ifFoo = b.ifStatement(fooId, b.blockStatement([
b.expressionStatement(b.callExpression(fooId, []))
]));
assert.ok(n.IfStatement.check(ifFoo));
assert.ok(n.Statement.check(ifFoo));
assert.ok(n.Node.check(ifFoo));
assert.ok(n.BlockStatement.check(ifFoo.consequent));
assert.strictEqual(
ifFoo.consequent.body[0].expression.arguments.length,
0,
);
assert.strictEqual(ifFoo.test, fooId);
assert.ok(n.Expression.check(ifFoo.test));
assert.ok(n.Identifier.check(ifFoo.test));
assert.ok(!n.Statement.check(ifFoo.test));
类型脚本
所发表的宣言来自与《公约》相同的定义
运行时间类型, 因此每个节点类型都可以作为类型Script类型
同名词下 namedTypes,以及 NodePath 财务报告和审定财务报表 Type 通用 :
import {
builders as b,
namedTypes as n,
visit,
NodePath,
} from "ast-types";
const fooId: n.Identifier = b.identifier("foo");
const call: n.CallExpression = b.callExpression(fooId, []);
// Visitor methods receive a NodePath parameterized by the node type, so
// path.node is narrowed for you.
visit(call, {
visitCallExpression(path) {
const callee: n.CallExpression["callee"] = path.node.callee;
this.traverse(path);
},
});
declare const somePath: NodePath<n.CallExpression>;
somePath.node.arguments;
注意节点类型中的 TypeScript 类型描述定义 接受,这往往是一个工会。 拓宽一个字段以承认一个节点形状 消费者可能需要缩小范围 变动情况 对于那些已经发生的.
AST 倾斜
因为它如此透彻地理解了AST类型系统,这个库 能够提供极佳的节点迭接和倒转机制.
如果你想要完全控制 转弯,你只需要一个方法
列出您 AST 节点的已知字段并获取其
价值,你可能会对原始人感兴趣 getFieldNames 财务报告和审定财务报表
getFieldValue数字 :
import {
getFieldNames,
getFieldValue,
} from "ast-types";
const partialFunExpr = { type: "FunctionExpression" };
// Even though partialFunExpr doesn't actually contain all the fields that
// are expected for a FunctionExpression, types.getFieldNames knows:
console.log(getFieldNames(partialFunExpr));
// [ 'type', 'id', 'params', 'body', 'generator', 'expression',
// 'defaults', 'rest', 'async' ]
// For fields that have default values, types.getFieldValue will return
// the default if the field is not actually defined.
console.log(getFieldValue(partialFunExpr, "generator"));
// false
两个低级的助手功能, eachField 财务报告和审定财务报表 someField,为
定义 getFieldNames 财务报告和审定财务报表 getFieldValue数字 :
// Iterate over all defined fields of an object, including those missing
// or undefined, passing each field name and effective value (as returned
// by getFieldValue) to the callback. If the object has no corresponding
// Def, the callback will never be called.
export function eachField(object, callback, context) {
getFieldNames(object).forEach(function(name) {
callback.call(this, name, getFieldValue(object, name));
}, context);
}
// Similar to eachField, except that iteration stops as soon as the
// callback returns a truthy value. Like Array.prototype.some, the final
// result is either true or false to indicates whether the callback
// returned true for any element or not.
export function someField(object, callback, context) {
return getFieldNames(object).some(function(name) {
return callback.call(this, name, getFieldValue(object, name));
}, context);
}
这就是你如何复制一个AST节点:
import { eachField } from "ast-types";
const copy = {};
eachField(node, function(name, value) {
// Note that undefined fields will be visited too, according to
// the rules associated with node.type, and default field values
// will be substituted if appropriate.
copy[name] = value;
})
但还不止这些! 您也可以轻松访问整个语法树
强者 types.visit 抽象化.
举个小例子来说明.. arguments.callee
从未在 ast数字 :
import assert from "assert";
import {
visit,
namedTypes as n,
} from "ast-types";
visit(ast, {
// This method will be called for any node with .type "MemberExpression":
visitMemberExpression(path) {
// Visitor methods receive a single argument, a NodePath object
// wrapping the node of interest.
var node = path.node;
if (
n.Identifier.check(node.object) &&
node.object.name === "arguments" &&
n.Identifier.check(node.property)
) {
assert.notStrictEqual(node.property.name, "callee");
}
// It's your responsibility to call this.traverse with some
// NodePath object (usually the one passed into the visitor
// method) before the visitor method returns, or return false to
// indicate that the traversal need not continue any further down
// this subtree.
this.traverse(path);
}
});
这里有一个 稍多参与的例子 改造 ...rest
参数进入浏览器运行的 ES5 JavaScript :
import assert from "assert";
import {
builders as b,
namedTypes as n,
visit,
} from "ast-types";
// Reuse the same AST structure for Array.prototype.slice.call.
var sliceExpr = b.memberExpression(
b.memberExpression(
b.memberExpression(
b.identifier("Array"),
b.identifier("prototype"),
false
),
b.identifier("slice"),
false
),
b.identifier("call"),
false
);
visit(ast, {
// This method will be called for any node whose type is a subtype of
// Function (e.g., FunctionDeclaration, FunctionExpression, and
// ArrowFunctionExpression). Note that types.visit precomputes a
// lookup table from every known type to the appropriate visitor
// method to call for nodes of that type, so the dispatch takes
// constant time.
visitFunction(path) {
// Visitor methods receive a single argument, a NodePath object
// wrapping the node of interest.
const node = path.node;
// It's your responsibility to call this.traverse with some
// NodePath object (usually the one passed into the visitor
// method) before the visitor method returns, or return false to
// indicate that the traversal need not continue any further down
// this subtree. An assertion will fail if you forget, which is
// awesome, because it means you will never again make the
// disastrous mistake of forgetting to traverse a subtree. Also
// cool: because you can call this method at any point in the
// visitor method, it's up to you whether your traversal is
// pre-order, post-order, or both!
this.traverse(path);
// This traversal is only concerned with Function nodes that have
// rest parameters.
if (!node.rest) {
return;
}
// For the purposes of this example, we won't worry about functions
// with Expression bodies.
n.BlockStatement.assert(node.body);
// Use types.builders to build a variable declaration of the form
//
// var rest = Array.prototype.slice.call(arguments, n);
//
// where `rest` is the name of the rest parameter, and `n` is a
// numeric literal specifying the number of named parameters the
// function takes.
const restVarDecl = b.variableDeclaration("var", [
b.variableDeclarator(
node.rest,
b.callExpression(sliceExpr, [
b.identifier("arguments"),
b.literal(node.params.length)
])
)
]);
// Similar to doing node.body.body.unshift(restVarDecl), except
// that the other NodePath objects wrapping body statements will
// have their indexes updated to accommodate the new statement.
path.get("body", "body").unshift(restVarDecl);
// Nullify node.rest now that we have simulated the behavior of
// the rest parameter using ordinary JavaScript.
path.get("rest").replace(null);
// There's nothing wrong with doing node.rest = null, but I wanted
// to point out that the above statement has the same effect.
assert.strictEqual(node.rest, null);
}
});
这是你可能使用 types.visit 用于执行一个函数
确定给定函数节点是否指的是 this数字 :
function usesThis(funcNode) {
n.Function.assert(funcNode);
var result = false;
visit(funcNode, {
visitThisExpression(path) {
result = true;
// The quickest way to terminate the traversal is to call
// this.abort(), which throws a special exception (instanceof
// this.AbortRequest) that will be caught in the top-level
// types.visit method, so you don't have to worry about
// catching the exception yourself.
this.abort();
},
visitFunction(path) {
// ThisExpression nodes in nested scopes don't count as `this`
// references for the original function node, so we can safely
// avoid traversing this subtree.
return false;
},
visitCallExpression(path) {
const node = path.node;
// If the function contains CallExpression nodes involving
// super, those expressions will implicitly depend on the
// value of `this`, even though they do not explicitly contain
// any ThisExpression nodes.
if (this.isSuperCallExpression(node)) {
result = true;
this.abort(); // Throws AbortRequest exception.
}
this.traverse(path);
},
// Yes, you can define arbitrary helper methods.
isSuperCallExpression(callExpr) {
n.CallExpression.assert(callExpr);
return this.isSuperIdentifier(callExpr.callee)
|| this.isSuperMemberExpression(callExpr.callee);
},
// And even helper helper methods!
isSuperIdentifier(node) {
return n.Identifier.check(node.callee)
&& node.callee.name === "super";
},
isSuperMemberExpression(node) {
return n.MemberExpression.check(node.callee)
&& n.Identifier.check(node.callee.object)
&& node.callee.object.name === "super";
}
});
return result;
}
你可能猜到,当一个 AbortRequest 是从树下抛出
例外将从相应的呼叫中传播到 this.traverse
在祖先的访客方法。 如果你决定取消
请求,只需抓住例外,并调用它 .cancel() 方法。 这个
其余树在树下 try- 说吧catch 块将废弃,但
祖先节点的其余兄弟姐妹将仍然被访问.
节点Path
这个 NodePath 对象传递给访问者的方法是 AST 周围的包装器
节点,它可以提供对祖先物体链的连接
(一路回AST根)和范围信息.
总的来说, path.node 是指被包裹的节点, path.parent.node
指最近一个 Node 祖师相传, path.parent.parent.node 页:1
外祖相等相传.
请注意: path.node 可能不是
path.parent.node例如,情况可能是: path.node 实值
属于父节点直接子的数组元素:
path.node === path.parent.node.elements[3]
在这种情况下,你应该知道 path.parentPath 提供
更精细地访问对象的完整路径(不只是 Node
(一) 从AST的根部:
// In reality, path.parent is the grandparent of path:
path.parentPath.parentPath === path.parent
// The path.parentPath object wraps the elements array (note that we use
// .value because the elements array is not a Node):
path.parentPath.value === path.parent.node.elements
// The path.node object is the fourth element in that array:
path.parentPath.value[3] === path.node
// Unlike path.node and path.value, which are synonyms because path.node
// is a Node object, path.parentPath.node is distinct from
// path.parentPath.value, because the elements array is not a
// Node. Instead, path.parentPath.node refers to the closest ancestor
// Node, which happens to be the same as path.parent.node:
path.parentPath.node === path.parent.node
// The path is named for its index in the elements array:
path.name === 3
// Likewise, path.parentPath is named for the property by which
// path.parent.node refers to it:
path.parentPath.name === "elements"
// Putting it all together, we can follow the chain of object references
// from path.parent.node all the way to path.node by accessing each
// property by name:
path.parent.node[path.parentPath.name][path.name] === path.node
这些 NodePath 对象是在无
修改 AST 节点本身, 因此如果相同的节点没有问题
多次出现在 AST 中( 如 Array.prototype.slice.call 内
因为它将用微调访问 NodePath
每次出现的时候.
儿童 NodePath 通过调用 .get 方法
父母关系 NodePath 对象 :
// If a NodePath object for the elements array has never been created
// before, it will be created here and cached in the future:
path.get("elements").get(3).value === path.value.elements[3]
// Alternatively, you can pass multiple property names to .get instead of
// chaining multiple .get calls:
path.get("elements", 0).value === path.value.elements[0]
NodePath 对象支持一些有用的方法:
// Replace one node with another node:
var fifth = path.get("elements", 4);
fifth.replace(newNode);
// Now do some stuff that might rearrange the list, and this replacement
// remains safe:
fifth.replace(newerNode);
// Replace the third element in an array with two new nodes:
path.get("elements", 2).replace(
b.identifier("foo"),
b.thisExpression()
);
// Remove a node and its parent if it would leave a redundant AST node:
//e.g. var t = 1, y =2; removing the `t` and `y` declarators results in `var undefined`.
path.prune(); //returns the closest parent `NodePath`.
// Remove a node from a list of nodes:
path.get("elements", 3).replace();
// Add three new nodes to the beginning of a list of nodes:
path.get("elements").unshift(a, b, c);
// Remove and return the first node in a list of nodes:
path.get("elements").shift();
// Push two new nodes onto the end of a list of nodes:
path.get("elements").push(d, e);
// Remove and return the last node in a list of nodes:
path.get("elements").pop();
// Insert a new node before/after the seventh node in a list of nodes:
var seventh = path.get("elements", 6);
seventh.insertBefore(newNode);
seventh.insertAfter(newNode);
// Insert a new element at index 5 in a list of nodes:
path.get("elements").insertAt(5, newNode);
范围
显示为 path.scope 在AST轨道提供时
关于可变和功能声明的信息
包含 path.node。见 范围.ts 为公众服务
接口,目前包括 .isGlobal, (中文(简体) ), .getGlobalScope(), (中文(简体) ),
.depth, (中文(简体) ), .declares(name), (中文(简体) ), .lookup(name),以及 .getBindings()。 。 。 .
自定义 AST 节点类型
这个 ast-types 模块设计要扩展。 为此
为指定新的AST节点类型提供可读、可声明的语法,
主要基于 require("ast-types").Type.def 函数 :
import {
Type,
builtInTypes,
builders as b,
finalize,
} from "ast-types";
const { def } = Type;
const { string } = builtInTypes;
// Suppose you need a named File type to wrap your Programs.
def("File")
.bases("Node")
.build("name", "program")
.field("name", string)
.field("program", def("Program"));
// Prevent further modifications to the File type (and any other
// types newly introduced by def(...)).
finalize();
// The b.file builder function is now available. It expects two
// arguments, as named by .build("name", "program") above.
const main = b.file("main.js", b.program([
// Pointless program contents included for extra color.
b.functionDeclaration(b.identifier("succ"), [
b.identifier("x")
], b.blockStatement([
b.returnStatement(
b.binaryExpression(
"+", b.identifier("x"), b.literal(1)
)
)
]))
]));
assert.strictEqual(main.name, "main.js");
assert.strictEqual(main.program.body[0].params[0].name, "x");
// etc.
// If you pass the wrong type of arguments, or fail to pass enough
// arguments, an AssertionError will be thrown.
b.file(b.blockStatement([]));
// ==> AssertionError: {"body":[],"type":"BlockStatement","loc":null} does not match type string
b.file("lib/types.js", b.thisExpression());
// ==> AssertionError: {"type":"ThisExpression","loc":null} does not match type Program
这个 def 语法用于定义所有默认 AST 节点类型
bayl-core.ts (英语), (中文(简体) ),
贝贝.ts, (中文(简体) ),
核心数.t, (中文(简体) ),
提议, (中文(简体) ),
es6. 吨级, (中文(简体) ),
es2016.ts (英语), (中文(简体) ),
es2017.ts (英语), (中文(简体) ),
2018.ts (英语), (中文(简体) ),
es2019.ts (英语), (中文(简体) ),
es2020.ts (英语), (中文(简体) ),
es2021.ts (英语), (中文(简体) ),
es2022.ts (英语), (中文(简体) ),
双子宫, (中文(简体) ),
流量.ts, (中文(简体) ),
页:1, (中文(简体) ),
类型注释.ts, (中文(简体) ),
财务报告和审定财务报表
类型标记.ts, (中文(简体) ),
所以,你已经
并不缺乏可以学习的例子.