API Reference
Complete API documentation for all AST toolkit packages.
Overview
The AST toolkit provides a clean, type-safe API for parsing JavaScript code and working with Abstract Syntax Trees.
Packages
@sylphlab/ast-core
Core types and interfaces for AST/CST nodes.
View @sylphlab/ast-core documentation →
@sylphlab/ast-javascript
JavaScript parser powered by ANTLR v4.
View @sylphlab/ast-javascript documentation →
Quick Reference
Parsing
import { parseJavaScript } from '@sylphlab/ast-javascript';
// Parse JavaScript code
const ast = parseJavaScript(code);Type Imports
import type { CstNode, Token, Position } from '@sylphlab/ast-core';
import type {
Program,
Statement,
Expression,
VariableDeclaration
} from '@sylphlab/ast-javascript';Core API
parseJavaScript()
Parse JavaScript source code into an AST.
function parseJavaScript(text: string): CstNode | nullParameters:
text: string- JavaScript source code
Returns:
CstNode | null- Root AST node, or null on parse error
Example:
const ast = parseJavaScript('const x = 42;');
if (ast) {
console.log('Type:', ast.type);
console.log('Text:', ast.text);
console.log('Position:', ast.position);
}Type Definitions
CstNode
Base interface for all CST (Concrete Syntax Tree) nodes.
interface CstNode {
type: string;
text: string;
position: Position;
children?: CstNode[];
}Properties:
type: string- Node type identifiertext: string- Exact source textposition: Position- Location in sourcechildren?: CstNode[]- Optional child nodes
Token
Base interface for leaf nodes (tokens).
interface Token extends CstNode {
children?: undefined;
}Tokens represent atomic units like keywords, identifiers, operators.
Position
Source location information.
interface Position {
startOffset: number;
endOffset: number;
}Properties:
startOffset: number- Start index (0-based)endOffset: number- End index (0-based, exclusive)
Point
Specific location in source.
interface Point {
offset: number;
}JavaScript Types
Program
Root node of a JavaScript AST.
interface Program extends JsNode {
type: 'Program';
body: Statement[];
}Statement Types
VariableDeclaration
interface VariableDeclaration extends Statement {
type: 'VariableDeclaration';
declarations: VariableDeclarator[];
kind: 'var' | 'let' | 'const';
}ExpressionStatement
interface ExpressionStatement extends Statement {
type: 'ExpressionStatement';
expression: Expression;
}BlockStatement
interface BlockStatement extends Statement {
type: 'BlockStatement';
body: Statement[];
}IfStatement
interface IfStatement extends Statement {
type: 'IfStatement';
test: Expression;
consequent: Statement;
alternate: Statement | null;
}Expression Types
AstIdentifier
interface AstIdentifier extends Expression, Pattern {
type: 'Identifier';
name: string;
}AstLiteral
interface AstLiteral extends Expression {
type: 'Literal';
value: string | number | boolean | null | RegExp;
raw?: string;
}Utility Functions
Tree Traversal
Walk through all nodes in an AST:
function traverse(
node: CstNode,
callback: (node: CstNode) => void
): void {
callback(node);
if (node.children) {
for (const child of node.children) {
traverse(child, callback);
}
}
}
// Usage
traverse(ast, (node) => {
console.log(node.type);
});Find Nodes by Type
Find all nodes of a specific type:
function findNodesByType(
root: CstNode,
type: string
): CstNode[] {
const results: CstNode[] = [];
traverse(root, (node) => {
if (node.type === type) {
results.push(node);
}
});
return results;
}
// Usage
const identifiers = findNodesByType(ast, 'Identifier');Extract Text
Get the full source text for a node:
function getSourceText(
node: CstNode,
source: string
): string {
return source.substring(
node.position.startOffset,
node.position.endOffset
);
}
// Usage
const code = 'const x = 42;';
const ast = parseJavaScript(code);
if (ast) {
const text = getSourceText(ast, code);
}Type Guards
Check node types safely:
function isToken(node: CstNode): node is Token {
return node.children === undefined;
}
function isStatement(node: CstNode): boolean {
return node.type.endsWith('Statement') ||
node.type.endsWith('Declaration');
}
function isExpression(node: CstNode): boolean {
return node.type.endsWith('Expression') ||
node.type === 'Identifier' ||
node.type === 'Literal';
}Advanced Usage
Custom Visitors
Implement a visitor pattern for AST traversal:
interface Visitor {
visitProgram?(node: Program): void;
visitVariableDeclaration?(node: VariableDeclaration): void;
visitIdentifier?(node: Identifier): void;
// Add more visit methods...
}
class MyVisitor implements Visitor {
visitProgram(node: Program): void {
console.log('Visiting program with', node.body.length, 'statements');
node.body.forEach(stmt => this.visit(stmt));
}
visitVariableDeclaration(node: VariableDeclaration): void {
console.log('Variable declaration:', node.kind);
}
visit(node: CstNode): void {
const methodName = `visit${node.type}`;
if (methodName in this) {
(this as any)[methodName](node);
}
if (node.children) {
node.children.forEach(child => this.visit(child));
}
}
}
// Usage
const visitor = new MyVisitor();
visitor.visit(ast);AST Transformation
Transform an AST:
function transformNode(
node: CstNode,
transformer: (node: CstNode) => CstNode
): CstNode {
const transformed = transformer(node);
if (transformed.children) {
return {
...transformed,
children: transformed.children.map(
child => transformNode(child, transformer)
)
};
}
return transformed;
}
// Usage: Rename all identifiers
const renamed = transformNode(ast, (node) => {
if (node.type === 'Identifier') {
return {
...node,
text: node.text + '_renamed'
};
}
return node;
});AST Filtering
Filter nodes based on criteria:
function filterNodes(
node: CstNode,
predicate: (node: CstNode) => boolean
): CstNode | null {
if (!predicate(node)) {
return null;
}
if (!node.children) {
return node;
}
const filteredChildren = node.children
.map(child => filterNodes(child, predicate))
.filter((child): child is CstNode => child !== null);
return {
...node,
children: filteredChildren
};
}
// Usage: Keep only identifiers
const onlyIdentifiers = filterNodes(ast, (node) =>
node.type === 'Program' ||
node.type === 'Identifier'
);Error Handling
Parse Errors
const ast = parseJavaScript('invalid @#$ syntax');
if (ast === null) {
console.error('Parse failed - syntax error');
} else {
console.log('Parse successful');
}Type Validation
function validateNode(node: CstNode): boolean {
if (!node.type || typeof node.type !== 'string') {
return false;
}
if (!node.text || typeof node.text !== 'string') {
return false;
}
if (!node.position ||
typeof node.position.startOffset !== 'number' ||
typeof node.position.endOffset !== 'number') {
return false;
}
return true;
}TypeScript Tips
Strict Type Checking
Enable strict mode in tsconfig.json:
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
}
}Type Assertions
Use type assertions when you know the node type:
const ast = parseJavaScript(code);
if (ast && ast.type === 'Program') {
const program = ast as Program;
program.body.forEach(statement => {
// Process statements
});
}Generic Helpers
Create generic helper functions:
function findFirst<T extends CstNode>(
root: CstNode,
predicate: (node: CstNode) => node is T
): T | null {
if (predicate(root)) {
return root;
}
if (root.children) {
for (const child of root.children) {
const found = findFirst(child, predicate);
if (found) return found;
}
}
return null;
}
// Usage
const firstIdentifier = findFirst(ast, (node): node is Identifier =>
node.type === 'Identifier'
);Performance Tips
1. Minimize Traversals
Collect multiple pieces of data in a single traversal:
interface Stats {
identifiers: number;
statements: number;
expressions: number;
}
function gatherStats(node: CstNode): Stats {
const stats: Stats = {
identifiers: 0,
statements: 0,
expressions: 0
};
traverse(node, (n) => {
if (n.type === 'Identifier') stats.identifiers++;
if (isStatement(n)) stats.statements++;
if (isExpression(n)) stats.expressions++;
});
return stats;
}2. Use Early Returns
Stop traversing when you find what you need:
function hasIdentifier(node: CstNode, name: string): boolean {
if (node.type === 'Identifier' && node.text === name) {
return true;
}
if (node.children) {
for (const child of node.children) {
if (hasIdentifier(child, name)) {
return true;
}
}
}
return false;
}3. Cache Results
Cache expensive computations:
const cache = new WeakMap<CstNode, any>();
function cachedComputation(node: CstNode): any {
if (cache.has(node)) {
return cache.get(node);
}
const result = expensiveOperation(node);
cache.set(node, result);
return result;
}Examples
Count Variables
function countVariables(ast: CstNode): number {
let count = 0;
traverse(ast, (node) => {
if (node.type === 'VariableDeclaration') {
count += (node as any).declarations?.length || 0;
}
});
return count;
}Extract Function Names
function extractFunctionNames(ast: CstNode): string[] {
const names: string[] = [];
traverse(ast, (node) => {
if (node.type === 'FunctionDeclaration') {
const nameNode = (node as any).id;
if (nameNode?.type === 'Identifier') {
names.push(nameNode.name);
}
}
});
return names;
}Find Unused Variables
function findUnusedVariables(ast: CstNode): string[] {
const declared = new Set<string>();
const used = new Set<string>();
traverse(ast, (node) => {
if (node.type === 'VariableDeclarator') {
const id = (node as any).id;
if (id?.type === 'Identifier') {
declared.add(id.name);
}
} else if (node.type === 'Identifier') {
used.add(node.text);
}
});
return Array.from(declared).filter(name => !used.has(name));
}Next Steps
Questions? Open an issue or contact us.