What is ast-types?
The ast-types npm package is designed to define and manipulate ASTs (Abstract Syntax Trees) efficiently. It provides a flexible way to inspect, transform, and generate code from ASTs, which is particularly useful in the context of building compilers, code analysis tools, and code transformers.
What are ast-types's main functionalities?
Defining AST node types
This feature allows users to define various types of AST nodes. The code sample shows how to define an 'Identifier' and a 'BinaryExpression' node, specifying their structure and inheritance.
const def = require('ast-types').Type.def;
def('Identifier')
.bases('Node')
.build('name');
def('BinaryExpression')
.bases('Expression')
.build('operator', 'left', 'right');
Building AST nodes
This feature enables the construction of AST nodes using predefined builders. The code sample demonstrates creating a binary expression that adds two literals.
const builders = require('ast-types').builders;
const b = builders;
const ast = b.binaryExpression('+', b.literal(1), b.literal(2));
Visiting AST nodes
This feature provides a mechanism to traverse and manipulate nodes in an AST. The code sample illustrates how to visit all identifier nodes in an AST and log their names.
const visit = require('ast-types').visit;
const ast = require('./some-ast');
visit(ast, {
visitIdentifier(path) {
console.log('Found an identifier:', path.node.name);
this.traverse(path);
}
});
Other packages similar to ast-types
babel-types
Similar to ast-types, babel-types is part of the Babel compiler ecosystem. It provides builders, validators, and converters for Babel's AST nodes. While ast-types is more generic and flexible, babel-types is specifically optimized for use with Babel's AST format.
recast
Recast is built on top of ast-types and provides additional utilities for parsing, printing, and source map support. It leverages ast-types for node type definitions and manipulation, but extends its functionality with a focus on maintaining exact original formatting.
AST Types
This module provides an efficient, modular,
Esprima-compatible implementation of
the abstract syntax
tree type hierarchy
pioneered by the Mozilla Parser
API.
Installation
From NPM:
npm install ast-types
From GitHub:
cd path/to/node_modules
git clone git://github.com/benjamn/ast-types.git
cd ast-types
npm install .
Basic Usage
var assert = require("assert");
var n = require("ast-types").namedTypes;
var b = require("ast-types").builders;
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));
AST Traversal
Because it understands the AST type system so thoroughly, this library
is able to provide excellent node iteration and traversal mechanisms.
Here's how you might iterate over the fields of an arbitrary AST node:
var copy = {};
require("ast-types").eachField(node, function(name, value) {
copy[name] = value;
})
If you want to perform a depth-first traversal of the entire AST,
that's also easy:
var types = require("ast-types");
var Literal = types.namedTypes.Literal;
var isString = types.builtInTypes.string;
var stringCounts = {};
require("ast-types").traverse(ast, function(node) {
if (Literal.check(node) && isString.check(node.value)) {
if (stringCounts.hasOwnProperty(node.value)) {
stringCounts[node.value] += 1;
} else {
stringCounts[node.value] = 1;
}
}
});
Here's an slightly deeper example demonstrating how to ignore certain
subtrees and inspect the node's ancestors:
var types = require("ast-types");
var namedTypes = types.namedTypes;
var isString = types.builtInTypes.string;
var thisProperties = {};
types.traverse(ast, function(node) {
if (namedTypes.FunctionExpression.check(node) ||
namedTypes.FunctionDeclaration.check(node)) {
return false;
}
if (namedTypes.ThisExpression.check(node) &&
namedTypes.MemberExpression.check(this.parent.node) &&
this.parent.node.object === node) {
var property = this.parent.node.property;
if (namedTypes.Identifier.check(property)) {
thisProperties[property.name] = true;
} else if (namedTypes.Literal.check(property) &&
isString.check(property.value)) {
thisProperties[property.value] = true;
}
}
});
Within the callback function, this
is always an instance of a simple
Path
type that has immutable .node
and .parent
properties. In
general, this.node
refers to the same node as the node
parameter,
this.parent.node
refers to the nearest Node
ancestor,
this.parent.parent.node
to the grandparent, and so on. These Path
objects are created during the traversal without modifying the AST
nodes themselves, so it's not a problem if the same node appears more
than once in the AST, because it will be visited with a distict Path
each time it appears.
Custom AST Node Types
The ast-types
module was designed to be extended. To that end, it
provides a readable, declarative syntax for specifying new AST node types,
based primarily upon the require("ast-types").Type.def
function:
var types = require("ast-types");
var def = types.Type.def;
var string = types.builtInTypes.string;
var b = types.builders;
def("File")
.bases("Node")
.build("name", "program")
.field("name", string)
.field("program", def("Program"));
types.finalize();
var main = b.file("main.js", b.program([
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");
b.file(b.blockStatement([]));
b.file("lib/types.js", b.thisExpression());
The def
syntax is used to define all the default AST node types found in
core.js,
es6.js,
mozilla.js,
e4x.js, and
fb-harmony.js, so you have
no shortage of examples to learn from.