The Book of AST
October 10, 2025 Β· View on GitHub
When I started dealing with AST I had a lot of problems in understanding basic parts. I read ESTree standard, I read Babel standard. They are very good! Anyways they put the information in a very hurd to understand for novice way.
So gradually I started admit some logic behind all of this. During development of πPutout and especially @putout/plugin-remove-unused-variables, I learned to work with all AST nodes. I thought I never finish and quit, but suddenly I covered all of them.
During development of π©ESCover I found out that a lot of nodes can be handled in the similar way, so there is a groups of them. Also it was unexpectedly for me that 14 nodes (with repeating structures in some of them) are enough to make a coverage tool!
With this guide I'm trying to supplement ESTree and Babel to make things a little bit easier for beginners.
Of course while using π¦PutoutScript you don't deal with AST directly most of the time. But for edge cases this information will be useful. Also πPutoutEditor will help you to jump in transformations on the speed of light π.
Have fun π and...
...let's the journey begin!
The river of Code is a long pool of code written in water. The best place to swim for πPutout. And when he not solving programming questions from friends, he usually swim or laying in the sun.
Green crocodile has friends, who makes first steps exploring the world of AST:
- π¦ Rhino likes to eat apples and read code from an apple tree.
- π¦ Owl likes to fly and read code from clouds.
- π Rabbit likes to eat carrots.
πPutout was in the AST world for a long time, so he got some experience and had a wish to share his knowledge with his friends so they was on the same level.
In AST world animals like to code, but they does it in a new way. They read others code most of the time and search for a way of transforming one peace of code to another.
For example here is the simplest transformation written in π¦PutoutScript, all animals familiar with:
__a = __b * __c + __b * __d -> __a = __b * (__c + __d);
All it does is switching places of variables, so the code:
animals = 'π¦' * 'π¦' + 'π¦' * 'π¦';
Will be transformed to:
animals = 'π¦' * ('π¦' + 'π¦');
That was introduction part, and now let's go to the point! Once upon a time πPutout swim over the river of Code and suddenly...
MemberExpression
π€Ώ deep dive
type MemberExpression = (object: Expression, property: Expression | Identifier, computed: boolean, optional: boolean) => Node;
MemberExpressionalways hasobjectandpropertyfields. Ifcomputedenabled thenpropertyisExpressionotherwise it isIdentifier.
...he was interrupted by writings on the water:
Friends['π']; // has computed value 'π'
Friends.elephant; // has not computed value 'elephant'
"Anytime you access a property of object or index of array you using MemberExpression", thought the crocodile wagging his tail.
Literal
π€Ώ deep dive
type Literal = (value: boolean | string | number | bigint | undefined | null) => Node;
Literalalways has avaluefield. Ifcomputedenabled thenpropertyisExpressionotherwise it isIdentifier.
"Anything can be expressed with Identifier or Literal!", - the voice said.
"Who is here?", - crocodile ask.
"I'm a big turtule π’, don't you remember me?"
"Why I should?"
"Maybe because you came here in your dreams?"
"I don't remember my dreams, anyways I'm here, and I don't agree with you!"
"About what?", - the turtle asked.
"Almost every other node depends on Identifiers and Literals"
"That's right!", - turtle said with a smile in her face.
Identifier
π€Ώ deep dive
type Identifier = (name: string) => Identifier;
When it's
- β not
Literal;- β not part of
Statement;- starts from
[a-zA-Z]and contains characters[a-zA-Z\d];It's
Identifierand most likely (but not necessarily) it's used as part of anExpression.
"Interesting that both object with properties and array with indexes can be Identifiers, or any other Expressions", continued his thought πPutout".
When the crocodile came ashore he saw his friend π¦Rhino chewing the apples π and staring at code on the tree π³:
const apple = 'π';
"Hi Pal!", Rhino said, "I can't understand the difference between Identifiers and Literals could you please help me?"
"Hi, Rhino!", answered πPutout, sure! Look
- β
apple- is Identifier; - β
'π'- is StringLiteral;
But the most interesting thing is const, because it is a Statement, and other parts of this code are Expressions".
Expression and Statement
"Expression and Statement it's like an apple π and a tree π³", πPutout said to his friend.
if (fruit === 'π')
// π³(π)
eat('π'); // π¦ β€οΈ π
"An apple can grow on a branch, but branch cannot grow on apple, the same goes to Expressions and Statements", continued πPutout.
"I like red apples", Rhino answered champing apples, "And now I understand the difference! Thank's a lot!"
"Your welcome!", πPutout said and joined in eating apples to his friend.
ArrayExpression and ArrayPattern
π€Ώ deep dive
type ArrayExpression = (elements: null[] | Expression[] | SpreadElement[]) => Node;
type ArrayPattern = (elements: null[] | PatternLike[]) => Node;
Both
ArrayExpressionandArrayPatterntakesproperties, both of which takesObjectProperty, but
ArrayExpressiontakes aselements:null,ExpressionandSpreadElement;ArrayPatterntakes aselements:ArrayPattern,AssignmentPattern,Identifier,ObjectPatternandRestElement;
Once the π¦Owl flied to πPutout when he leying in the sun after lunch.
"Hi greany!", owl said, "I just saw a very strange thing on the cloud! Need you help, it drives me crazy!"
"Hi feathered! Tell me what the deal?", crocodile answered and rolled over to the other side.
const birds = ['π¦'];
const [owl] = birds;
"What the difference between this two lines?", π¦Owl asked.
"First one is ArrayExpression with one element that is StringLiteral 'π¦'", started πPutout. "And second one is ArrayPattern with one element that is Identifier owl.
"So pattern always on the left side, and expression on the right side?", the owl asked thoughtfully.
"Exactly! For destructuring we always use patterns", answered πPutout, "same goes to ObjectExpression and ObjectPattern".
ObjectExpression and ObjectPattern
π€Ώ deep dive
type ObjectExpression = (properties: ObjectMethod[] | ObjectProperty[] | SpreadElement[]) => Node;
type ObjectPattern = (properties: RestElement[] | ObjectProperty[]) => Node;
Both
ObjectExpressionandObjectPatterntakesproperties, both of which takesObjectProperty, but
ObjectExpressiontakes asproperties:ObjectMethodandSpreadElement;ObjectPatterntakes asproperties:RestElementonly;
Owl scratched her paw on the ground:
const birds = {
owl: 'π¦',
};
const {owl} = birds;
And sayed, "Here is the other code I saw in the cloud, is it similar on any kind?"
"Sure!", purred crocodile, "Now owl is a key of ObjectProperty of ObjectExpression in the first line, and second line have ObjectPattern."
"That's so simple!", Owl said, "Thank you so much πPutout! These notations can be confusing, but you always know how to unravel this tangle π§Ά"!
"Always welcome!", πPutout said, merrily waving his tail, he did not yet know that a rabbit would appear in a few moments ...
SpreadElement and RestElement
π€Ώ deep dive
type SpreadElement = (argument: Expression) => Node;
type RestElement = (argument: Expression) => Node;
Spread syntax (
...) usually takesArrayExpressionorObjectExpressionto be expanded in places where zero or more items are expected.The rest parameter syntax allows a function to accept an indefinite number of arguments as an
array.(c) MDN
"Hi pal!", π Rabbit with a full mouth of carrots π₯ says.
"Hi Rabbit!", said Putout, "What a surprise!".
"Today I saw the code placed from carrots that looks this way", said Rabbit and started to placing carrots. "Look!", he said when over:
const funny = animals;
say(...[
'π',
'π₯',
]);
function say(...vegatables) {}
"Tell me please something about it!".
"Well, SpreadElement takes any Expression as an argument, and takes items that metters from animals and put them to funny, or call a FunctionExpression say and passing vegatables as an arguments. It can even collect all arguments into a variable vegetable with help of RestElement.
"Nice!", said Rabbit keeping eating carrots, "Now things got more clear to me! Take a carrot, friend, they so sweet I can't break away!".
"Thank you to, Rabbit", Putout answered and take carrot, he loved fruits and vegatables and was always happy to eat it.
Unexpectedly a Wise turtule π’ appeared from the bush...